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;
45029 this.el.dom.value = state ? this.inputValue : this.valueOff;
45031 this.inSetChecked = false;
45034 // handle setting of hidden value by some other method!!?!?
45035 setFromHidden: function()
45040 //console.log("SET FROM HIDDEN");
45041 //alert('setFrom hidden');
45042 this.setValue(this.el.dom.value);
45045 onDestroy : function()
45048 Roo.get(this.viewEl).remove();
45051 Roo.form.Checkbox.superclass.onDestroy.call(this);
45054 setBoxLabel : function(str)
45056 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
45061 * Ext JS Library 1.1.1
45062 * Copyright(c) 2006-2007, Ext JS, LLC.
45064 * Originally Released Under LGPL - original licence link has changed is not relivant.
45067 * <script type="text/javascript">
45071 * @class Roo.form.Radio
45072 * @extends Roo.form.Checkbox
45073 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
45074 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
45076 * Creates a new Radio
45077 * @param {Object} config Configuration options
45079 Roo.form.Radio = function(){
45080 Roo.form.Radio.superclass.constructor.apply(this, arguments);
45082 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
45083 inputType: 'radio',
45086 * If this radio is part of a group, it will return the selected value
45089 getGroupValue : function(){
45090 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
45094 onRender : function(ct, position){
45095 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45097 if(this.inputValue !== undefined){
45098 this.el.dom.value = this.inputValue;
45101 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
45102 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
45103 //var viewEl = this.wrap.createChild({
45104 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
45105 //this.viewEl = viewEl;
45106 //this.wrap.on('click', this.onClick, this);
45108 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
45109 //this.el.on('propertychange', this.setFromHidden, this); //ie
45114 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
45115 // viewEl.on('click', this.onClick, this);
45118 this.el.dom.checked = 'checked' ;
45123 * Sets the checked state of the checkbox.
45124 * On is always based on a string comparison between inputValue and the param.
45125 * @param {Boolean/String} value - the value to set
45126 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45128 setValue : function(v,suppressEvent){
45131 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
45132 //if(this.el && this.el.dom){
45133 // this.el.dom.checked = this.checked;
45134 // this.el.dom.defaultChecked = this.checked;
45136 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
45138 this.el.dom.form[this.name].value = v;
45140 //this.fireEvent("check", this, this.checked);
45143 setChecked : function(state,suppressEvent)
45147 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
45149 this.checked = state;
45150 if(suppressEvent !== true){
45151 this.fireEvent('check', this, state);
45158 reset : function(){
45159 // this.setValue(this.resetValue);
45160 //this.originalValue = this.getValue();
45161 this.clearInvalid();
45164 });Roo.rtf = {}; // namespace
45165 Roo.rtf.Hex = function(hex)
45169 Roo.rtf.Paragraph = function(opts)
45171 this.content = []; ///??? is that used?
45172 };Roo.rtf.Span = function(opts)
45174 this.value = opts.value;
45177 Roo.rtf.Group = function(parent)
45179 // we dont want to acutally store parent - it will make debug a nightmare..
45187 Roo.rtf.Group.prototype = {
45191 addContent : function(node) {
45192 // could set styles...
45193 this.content.push(node);
45195 addChild : function(cn)
45199 // only for images really...
45200 toDataURL : function()
45202 var mimetype = false;
45204 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
45205 mimetype = "image/png";
45207 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
45208 mimetype = "image/jpeg";
45211 return 'about:blank'; // ?? error?
45215 var hexstring = this.content[this.content.length-1].value;
45217 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
45218 return String.fromCharCode(parseInt(a, 16));
45223 // this looks like it's normally the {rtf{ .... }}
45224 Roo.rtf.Document = function()
45226 // we dont want to acutally store parent - it will make debug a nightmare..
45232 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
45233 addChild : function(cn)
45237 case 'rtlch': // most content seems to be inside this??
45240 this.rtlch.push(cn);
45243 this[cn.type] = cn;
45248 getElementsByType : function(type)
45251 this._getElementsByType(type, ret, this.cn, 'rtf');
45254 _getElementsByType : function (type, ret, search_array, path)
45256 search_array.forEach(function(n,i) {
45257 if (n.type == type) {
45258 n.path = path + '/' + n.type + ':' + i;
45261 if (n.cn.length > 0) {
45262 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
45269 Roo.rtf.Ctrl = function(opts)
45271 this.value = opts.value;
45272 this.param = opts.param;
45277 * based on this https://github.com/iarna/rtf-parser
45278 * it's really only designed to extract pict from pasted RTF
45282 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45291 Roo.rtf.Parser = function(text) {
45292 //super({objectMode: true})
45294 this.parserState = this.parseText;
45296 // these are for interpeter...
45298 ///this.parserState = this.parseTop
45299 this.groupStack = [];
45300 this.hexStore = [];
45303 this.groups = []; // where we put the return.
45305 for (var ii = 0; ii < text.length; ++ii) {
45308 if (text[ii] === '\n') {
45314 this.parserState(text[ii]);
45320 Roo.rtf.Parser.prototype = {
45321 text : '', // string being parsed..
45323 controlWordParam : '',
45327 groupStack : false,
45332 row : 1, // reportin?
45336 push : function (el)
45338 var m = 'cmd'+ el.type;
45339 if (typeof(this[m]) == 'undefined') {
45340 Roo.log('invalid cmd:' + el.type);
45346 flushHexStore : function()
45348 if (this.hexStore.length < 1) {
45351 var hexstr = this.hexStore.map(
45356 this.group.addContent( new Roo.rtf.Hex( hexstr ));
45359 this.hexStore.splice(0)
45363 cmdgroupstart : function()
45365 this.flushHexStore();
45367 this.groupStack.push(this.group);
45370 if (this.doc === false) {
45371 this.group = this.doc = new Roo.rtf.Document();
45375 this.group = new Roo.rtf.Group(this.group);
45377 cmdignorable : function()
45379 this.flushHexStore();
45380 this.group.ignorable = true;
45382 cmdendparagraph : function()
45384 this.flushHexStore();
45385 this.group.addContent(new Roo.rtf.Paragraph());
45387 cmdgroupend : function ()
45389 this.flushHexStore();
45390 var endingGroup = this.group;
45393 this.group = this.groupStack.pop();
45395 this.group.addChild(endingGroup);
45400 var doc = this.group || this.doc;
45401 //if (endingGroup instanceof FontTable) {
45402 // doc.fonts = endingGroup.table
45403 //} else if (endingGroup instanceof ColorTable) {
45404 // doc.colors = endingGroup.table
45405 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45406 if (endingGroup.ignorable === false) {
45408 this.groups.push(endingGroup);
45409 // Roo.log( endingGroup );
45411 //Roo.each(endingGroup.content, function(item)) {
45412 // doc.addContent(item);
45414 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45417 cmdtext : function (cmd)
45419 this.flushHexStore();
45420 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45421 //this.group = this.doc
45422 return; // we really don't care about stray text...
45424 this.group.addContent(new Roo.rtf.Span(cmd));
45426 cmdcontrolword : function (cmd)
45428 this.flushHexStore();
45429 if (!this.group.type) {
45430 this.group.type = cmd.value;
45433 this.group.addContent(new Roo.rtf.Ctrl(cmd));
45434 // we actually don't care about ctrl words...
45437 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45438 if (this[method]) {
45439 this[method](cmd.param)
45441 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45445 cmdhexchar : function(cmd) {
45446 this.hexStore.push(cmd);
45448 cmderror : function(cmd) {
45454 if (this.text !== '\u0000') this.emitText()
45460 parseText : function(c)
45463 this.parserState = this.parseEscapes;
45464 } else if (c === '{') {
45465 this.emitStartGroup();
45466 } else if (c === '}') {
45467 this.emitEndGroup();
45468 } else if (c === '\x0A' || c === '\x0D') {
45469 // cr/lf are noise chars
45475 parseEscapes: function (c)
45477 if (c === '\\' || c === '{' || c === '}') {
45479 this.parserState = this.parseText;
45481 this.parserState = this.parseControlSymbol;
45482 this.parseControlSymbol(c);
45485 parseControlSymbol: function(c)
45488 this.text += '\u00a0'; // nbsp
45489 this.parserState = this.parseText
45490 } else if (c === '-') {
45491 this.text += '\u00ad'; // soft hyphen
45492 } else if (c === '_') {
45493 this.text += '\u2011'; // non-breaking hyphen
45494 } else if (c === '*') {
45495 this.emitIgnorable();
45496 this.parserState = this.parseText;
45497 } else if (c === "'") {
45498 this.parserState = this.parseHexChar;
45499 } else if (c === '|') { // formula cacter
45500 this.emitFormula();
45501 this.parserState = this.parseText;
45502 } else if (c === ':') { // subentry in an index entry
45503 this.emitIndexSubEntry();
45504 this.parserState = this.parseText;
45505 } else if (c === '\x0a') {
45506 this.emitEndParagraph();
45507 this.parserState = this.parseText;
45508 } else if (c === '\x0d') {
45509 this.emitEndParagraph();
45510 this.parserState = this.parseText;
45512 this.parserState = this.parseControlWord;
45513 this.parseControlWord(c);
45516 parseHexChar: function (c)
45518 if (/^[A-Fa-f0-9]$/.test(c)) {
45520 if (this.hexChar.length >= 2) {
45521 this.emitHexChar();
45522 this.parserState = this.parseText;
45526 this.emitError("Invalid character \"" + c + "\" in hex literal.");
45527 this.parserState = this.parseText;
45530 parseControlWord : function(c)
45533 this.emitControlWord();
45534 this.parserState = this.parseText;
45535 } else if (/^[-\d]$/.test(c)) {
45536 this.parserState = this.parseControlWordParam;
45537 this.controlWordParam += c;
45538 } else if (/^[A-Za-z]$/.test(c)) {
45539 this.controlWord += c;
45541 this.emitControlWord();
45542 this.parserState = this.parseText;
45546 parseControlWordParam : function (c) {
45547 if (/^\d$/.test(c)) {
45548 this.controlWordParam += c;
45549 } else if (c === ' ') {
45550 this.emitControlWord();
45551 this.parserState = this.parseText;
45553 this.emitControlWord();
45554 this.parserState = this.parseText;
45562 emitText : function () {
45563 if (this.text === '') {
45575 emitControlWord : function ()
45578 if (this.controlWord === '') {
45579 // do we want to track this - it seems just to cause problems.
45580 //this.emitError('empty control word');
45583 type: 'controlword',
45584 value: this.controlWord,
45585 param: this.controlWordParam !== '' && Number(this.controlWordParam),
45591 this.controlWord = '';
45592 this.controlWordParam = '';
45594 emitStartGroup : function ()
45598 type: 'groupstart',
45604 emitEndGroup : function ()
45614 emitIgnorable : function ()
45624 emitHexChar : function ()
45629 value: this.hexChar,
45636 emitError : function (message)
45644 char: this.cpos //,
45645 //stack: new Error().stack
45648 emitEndParagraph : function () {
45651 type: 'endparagraph',
45659 Roo.htmleditor = {};
45662 * @class Roo.htmleditor.Filter
45663 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45664 * @cfg {DomElement} node The node to iterate and filter
45665 * @cfg {boolean|String|Array} tag Tags to replace
45667 * Create a new Filter.
45668 * @param {Object} config Configuration options
45673 Roo.htmleditor.Filter = function(cfg) {
45674 Roo.apply(this.cfg);
45675 // this does not actually call walk as it's really just a abstract class
45679 Roo.htmleditor.Filter.prototype = {
45685 // overrride to do replace comments.
45686 replaceComment : false,
45688 // overrride to do replace or do stuff with tags..
45689 replaceTag : false,
45691 walk : function(dom)
45693 Roo.each( Array.from(dom.childNodes), function( e ) {
45696 case e.nodeType == 8 && this.replaceComment !== false: // comment
45697 this.replaceComment(e);
45700 case e.nodeType != 1: //not a node.
45703 case this.tag === true: // everything
45704 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
45705 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
45706 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45707 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45708 if (this.replaceTag && false === this.replaceTag(e)) {
45711 if (e.hasChildNodes()) {
45716 default: // tags .. that do not match.
45717 if (e.hasChildNodes()) {
45727 removeNodeKeepChildren : function( node)
45730 ar = Array.from(node.childNodes);
45731 for (var i = 0; i < ar.length; i++) {
45733 node.removeChild(ar[i]);
45734 // what if we need to walk these???
45735 node.parentNode.insertBefore(ar[i], node);
45738 node.parentNode.removeChild(node);
45743 * @class Roo.htmleditor.FilterAttributes
45744 * clean attributes and styles including http:// etc.. in attribute
45746 * Run a new Attribute Filter
45747 * @param {Object} config Configuration options
45749 Roo.htmleditor.FilterAttributes = function(cfg)
45751 Roo.apply(this, cfg);
45752 this.attrib_black = this.attrib_black || [];
45753 this.attrib_white = this.attrib_white || [];
45755 this.attrib_clean = this.attrib_clean || [];
45756 this.style_white = this.style_white || [];
45757 this.style_black = this.style_black || [];
45758 this.walk(cfg.node);
45761 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45763 tag: true, // all tags
45765 attrib_black : false, // array
45766 attrib_clean : false,
45767 attrib_white : false,
45769 style_white : false,
45770 style_black : false,
45773 replaceTag : function(node)
45775 if (!node.attributes || !node.attributes.length) {
45779 for (var i = node.attributes.length-1; i > -1 ; i--) {
45780 var a = node.attributes[i];
45782 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45783 node.removeAttribute(a.name);
45789 if (a.name.toLowerCase().substr(0,2)=='on') {
45790 node.removeAttribute(a.name);
45795 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45796 node.removeAttribute(a.name);
45799 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45800 this.cleanAttr(node,a.name,a.value); // fixme..
45803 if (a.name == 'style') {
45804 this.cleanStyle(node,a.name,a.value);
45807 /// clean up MS crap..
45808 // tecnically this should be a list of valid class'es..
45811 if (a.name == 'class') {
45812 if (a.value.match(/^Mso/)) {
45813 node.removeAttribute('class');
45816 if (a.value.match(/^body$/)) {
45817 node.removeAttribute('class');
45827 return true; // clean children
45830 cleanAttr: function(node, n,v)
45833 if (v.match(/^\./) || v.match(/^\//)) {
45836 if (v.match(/^(http|https):\/\//)
45837 || v.match(/^mailto:/)
45838 || v.match(/^ftp:/)
45839 || v.match(/^data:/)
45843 if (v.match(/^#/)) {
45846 if (v.match(/^\{/)) { // allow template editing.
45849 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45850 node.removeAttribute(n);
45853 cleanStyle : function(node, n,v)
45855 if (v.match(/expression/)) { //XSS?? should we even bother..
45856 node.removeAttribute(n);
45860 var parts = v.split(/;/);
45863 Roo.each(parts, function(p) {
45864 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45868 var l = p.split(':').shift().replace(/\s+/g,'');
45869 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45871 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45875 // only allow 'c whitelisted system attributes'
45876 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45884 if (clean.length) {
45885 node.setAttribute(n, clean.join(';'));
45887 node.removeAttribute(n);
45896 * @class Roo.htmleditor.FilterBlack
45897 * remove blacklisted elements.
45899 * Run a new Blacklisted Filter
45900 * @param {Object} config Configuration options
45903 Roo.htmleditor.FilterBlack = function(cfg)
45905 Roo.apply(this, cfg);
45906 this.walk(cfg.node);
45909 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45911 tag : true, // all elements.
45913 replaceTag : function(n)
45915 n.parentNode.removeChild(n);
45919 * @class Roo.htmleditor.FilterComment
45922 * Run a new Comments Filter
45923 * @param {Object} config Configuration options
45925 Roo.htmleditor.FilterComment = function(cfg)
45927 this.walk(cfg.node);
45930 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45933 replaceComment : function(n)
45935 n.parentNode.removeChild(n);
45938 * @class Roo.htmleditor.FilterKeepChildren
45939 * remove tags but keep children
45941 * Run a new Keep Children Filter
45942 * @param {Object} config Configuration options
45945 Roo.htmleditor.FilterKeepChildren = function(cfg)
45947 Roo.apply(this, cfg);
45948 if (this.tag === false) {
45949 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45952 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
45953 this.cleanNamespace = true;
45956 this.walk(cfg.node);
45959 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45961 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
45963 replaceTag : function(node)
45965 // walk children...
45966 //Roo.log(node.tagName);
45967 var ar = Array.from(node.childNodes);
45970 for (var i = 0; i < ar.length; i++) {
45972 if (e.nodeType == 1) {
45974 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
45975 || // array and it matches
45976 (typeof(this.tag) == 'string' && this.tag == e.tagName)
45978 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
45980 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
45982 this.replaceTag(ar[i]); // child is blacklisted as well...
45987 ar = Array.from(node.childNodes);
45988 for (var i = 0; i < ar.length; i++) {
45990 node.removeChild(ar[i]);
45991 // what if we need to walk these???
45992 node.parentNode.insertBefore(ar[i], node);
45993 if (this.tag !== false) {
45998 //Roo.log("REMOVE:" + node.tagName);
45999 node.parentNode.removeChild(node);
46000 return false; // don't walk children
46005 * @class Roo.htmleditor.FilterParagraph
46006 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
46007 * like on 'push' to remove the <p> tags and replace them with line breaks.
46009 * Run a new Paragraph Filter
46010 * @param {Object} config Configuration options
46013 Roo.htmleditor.FilterParagraph = function(cfg)
46015 // no need to apply config.
46016 this.walk(cfg.node);
46019 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
46026 replaceTag : function(node)
46029 if (node.childNodes.length == 1 &&
46030 node.childNodes[0].nodeType == 3 &&
46031 node.childNodes[0].textContent.trim().length < 1
46033 // remove and replace with '<BR>';
46034 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
46035 return false; // no need to walk..
46037 var ar = Array.from(node.childNodes);
46038 for (var i = 0; i < ar.length; i++) {
46039 node.removeChild(ar[i]);
46040 // what if we need to walk these???
46041 node.parentNode.insertBefore(ar[i], node);
46043 // now what about this?
46047 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
46048 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
46049 node.parentNode.removeChild(node);
46056 * @class Roo.htmleditor.FilterSpan
46057 * filter span's with no attributes out..
46059 * Run a new Span Filter
46060 * @param {Object} config Configuration options
46063 Roo.htmleditor.FilterSpan = function(cfg)
46065 // no need to apply config.
46066 this.walk(cfg.node);
46069 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
46075 replaceTag : function(node)
46077 if (node.attributes && node.attributes.length > 0) {
46078 return true; // walk if there are any.
46080 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
46086 * @class Roo.htmleditor.FilterTableWidth
46087 try and remove table width data - as that frequently messes up other stuff.
46089 * was cleanTableWidths.
46091 * Quite often pasting from word etc.. results in tables with column and widths.
46092 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
46095 * Run a new Table Filter
46096 * @param {Object} config Configuration options
46099 Roo.htmleditor.FilterTableWidth = function(cfg)
46101 // no need to apply config.
46102 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
46103 this.walk(cfg.node);
46106 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
46111 replaceTag: function(node) {
46115 if (node.hasAttribute('width')) {
46116 node.removeAttribute('width');
46120 if (node.hasAttribute("style")) {
46123 var styles = node.getAttribute("style").split(";");
46125 Roo.each(styles, function(s) {
46126 if (!s.match(/:/)) {
46129 var kv = s.split(":");
46130 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
46133 // what ever is left... we allow.
46136 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46137 if (!nstyle.length) {
46138 node.removeAttribute('style');
46142 return true; // continue doing children..
46145 * @class Roo.htmleditor.FilterWord
46146 * try and clean up all the mess that Word generates.
46148 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
46151 * Run a new Span Filter
46152 * @param {Object} config Configuration options
46155 Roo.htmleditor.FilterWord = function(cfg)
46157 // no need to apply config.
46158 this.replaceDocBullets(cfg.node);
46160 this.replaceAname(cfg.node);
46161 // this is disabled as the removal is done by other filters;
46162 // this.walk(cfg.node);
46167 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
46173 * Clean up MS wordisms...
46175 replaceTag : function(node)
46178 // no idea what this does - span with text, replaceds with just text.
46180 node.nodeName == 'SPAN' &&
46181 !node.hasAttributes() &&
46182 node.childNodes.length == 1 &&
46183 node.firstChild.nodeName == "#text"
46185 var textNode = node.firstChild;
46186 node.removeChild(textNode);
46187 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
46188 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
46190 node.parentNode.insertBefore(textNode, node);
46191 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
46192 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
46195 node.parentNode.removeChild(node);
46196 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
46201 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
46202 node.parentNode.removeChild(node);
46203 return false; // dont do chidlren
46205 //Roo.log(node.tagName);
46206 // remove - but keep children..
46207 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
46208 //Roo.log('-- removed');
46209 while (node.childNodes.length) {
46210 var cn = node.childNodes[0];
46211 node.removeChild(cn);
46212 node.parentNode.insertBefore(cn, node);
46213 // move node to parent - and clean it..
46214 if (cn.nodeType == 1) {
46215 this.replaceTag(cn);
46219 node.parentNode.removeChild(node);
46220 /// no need to iterate chidlren = it's got none..
46221 //this.iterateChildren(node, this.cleanWord);
46222 return false; // no need to iterate children.
46225 if (node.className.length) {
46227 var cn = node.className.split(/\W+/);
46229 Roo.each(cn, function(cls) {
46230 if (cls.match(/Mso[a-zA-Z]+/)) {
46235 node.className = cna.length ? cna.join(' ') : '';
46237 node.removeAttribute("class");
46241 if (node.hasAttribute("lang")) {
46242 node.removeAttribute("lang");
46245 if (node.hasAttribute("style")) {
46247 var styles = node.getAttribute("style").split(";");
46249 Roo.each(styles, function(s) {
46250 if (!s.match(/:/)) {
46253 var kv = s.split(":");
46254 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
46257 // what ever is left... we allow.
46260 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46261 if (!nstyle.length) {
46262 node.removeAttribute('style');
46265 return true; // do children
46271 styleToObject: function(node)
46273 var styles = (node.getAttribute("style") || '').split(";");
46275 Roo.each(styles, function(s) {
46276 if (!s.match(/:/)) {
46279 var kv = s.split(":");
46281 // what ever is left... we allow.
46282 ret[kv[0].trim()] = kv[1];
46288 replaceAname : function (doc)
46290 // replace all the a/name without..
46291 var aa = Array.from(doc.getElementsByTagName('a'));
46292 for (var i = 0; i < aa.length; i++) {
46294 if (a.hasAttribute("name")) {
46295 a.removeAttribute("name");
46297 if (a.hasAttribute("href")) {
46300 // reparent children.
46301 this.removeNodeKeepChildren(a);
46311 replaceDocBullets : function(doc)
46313 // this is a bit odd - but it appears some indents use ql-indent-1
46314 //Roo.log(doc.innerHTML);
46316 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
46317 for( var i = 0; i < listpara.length; i ++) {
46318 listpara[i].className = "MsoListParagraph";
46321 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
46322 for( var i = 0; i < listpara.length; i ++) {
46323 listpara[i].className = "MsoListParagraph";
46325 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
46326 for( var i = 0; i < listpara.length; i ++) {
46327 listpara[i].className = "MsoListParagraph";
46329 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
46330 for( var i = 0; i < listpara.length; i ++) {
46331 listpara[i].className = "MsoListParagraph";
46334 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
46335 var htwo = Array.from(doc.getElementsByTagName('h2'));
46336 for( var i = 0; i < htwo.length; i ++) {
46337 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
46338 htwo[i].className = "MsoListParagraph";
46341 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
46342 for( var i = 0; i < listpara.length; i ++) {
46343 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
46344 listpara[i].className = "MsoListParagraph";
46346 listpara[i].className = "MsoNormalx";
46350 listpara = doc.getElementsByClassName('MsoListParagraph');
46351 // Roo.log(doc.innerHTML);
46355 while(listpara.length) {
46357 this.replaceDocBullet(listpara.item(0));
46364 replaceDocBullet : function(p)
46366 // gather all the siblings.
46368 parent = p.parentNode,
46369 doc = parent.ownerDocument,
46372 var listtype = 'ul';
46374 if (ns.nodeType != 1) {
46375 ns = ns.nextSibling;
46378 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
46381 var spans = ns.getElementsByTagName('span');
46382 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
46384 ns = ns.nextSibling;
46386 if (spans.length && spans[0].hasAttribute('style')) {
46387 var style = this.styleToObject(spans[0]);
46388 if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
46395 var spans = ns.getElementsByTagName('span');
46396 if (!spans.length) {
46399 var has_list = false;
46400 for(var i = 0; i < spans.length; i++) {
46401 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
46410 ns = ns.nextSibling;
46414 if (!items.length) {
46419 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
46420 parent.insertBefore(ul, p);
46422 var stack = [ ul ];
46423 var last_li = false;
46425 var margin_to_depth = {};
46428 items.forEach(function(n, ipos) {
46429 //Roo.log("got innertHMLT=" + n.innerHTML);
46431 var spans = n.getElementsByTagName('span');
46432 if (!spans.length) {
46433 //Roo.log("No spans found");
46435 parent.removeChild(n);
46438 return; // skip it...
46444 for(var i = 0; i < spans.length; i++) {
46446 style = this.styleToObject(spans[i]);
46447 if (typeof(style['mso-list']) == 'undefined') {
46450 if (listtype == 'ol') {
46451 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
46453 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
46456 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
46457 style = this.styleToObject(n); // mo-list is from the parent node.
46458 if (typeof(style['mso-list']) == 'undefined') {
46459 //Roo.log("parent is missing level");
46461 parent.removeChild(n);
46466 var margin = style['margin-left'];
46467 if (typeof(margin_to_depth[margin]) == 'undefined') {
46469 margin_to_depth[margin] = max_margins;
46471 nlvl = margin_to_depth[margin] ;
46475 var nul = doc.createElement(listtype); // what about number lists...
46477 last_li = doc.createElement('li');
46478 stack[lvl].appendChild(last_li);
46480 last_li.appendChild(nul);
46486 // not starting at 1..
46487 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
46488 stack[nlvl].setAttribute("start", num);
46491 var nli = stack[nlvl].appendChild(doc.createElement('li'));
46493 nli.innerHTML = n.innerHTML;
46494 //Roo.log("innerHTML = " + n.innerHTML);
46495 parent.removeChild(n);
46511 * @class Roo.htmleditor.FilterStyleToTag
46512 * part of the word stuff... - certain 'styles' should be converted to tags.
46514 * font-weight: bold -> bold
46515 * ?? super / subscrit etc..
46518 * Run a new style to tag filter.
46519 * @param {Object} config Configuration options
46521 Roo.htmleditor.FilterStyleToTag = function(cfg)
46525 B : [ 'fontWeight' , 'bold'],
46526 I : [ 'fontStyle' , 'italic'],
46527 //pre : [ 'font-style' , 'italic'],
46528 // h1.. h6 ?? font-size?
46529 SUP : [ 'verticalAlign' , 'super' ],
46530 SUB : [ 'verticalAlign' , 'sub' ]
46535 Roo.apply(this, cfg);
46538 this.walk(cfg.node);
46545 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
46547 tag: true, // all tags
46552 replaceTag : function(node)
46556 if (node.getAttribute("style") === null) {
46560 for (var k in this.tags) {
46561 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46563 node.style.removeProperty(this.tags[k][0]);
46566 if (!inject.length) {
46569 var cn = Array.from(node.childNodes);
46571 Roo.each(inject, function(t) {
46572 var nc = node.ownerDocument.createElement(t);
46573 nn.appendChild(nc);
46576 for(var i = 0;i < cn.length;cn++) {
46577 node.removeChild(cn[i]);
46578 nn.appendChild(cn[i]);
46580 return true /// iterate thru
46584 * @class Roo.htmleditor.FilterLongBr
46585 * BR/BR/BR - keep a maximum of 2...
46587 * Run a new Long BR Filter
46588 * @param {Object} config Configuration options
46591 Roo.htmleditor.FilterLongBr = function(cfg)
46593 // no need to apply config.
46594 this.walk(cfg.node);
46597 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46604 replaceTag : function(node)
46607 var ps = node.nextSibling;
46608 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46609 ps = ps.nextSibling;
46612 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
46613 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46617 if (!ps || ps.nodeType != 1) {
46621 if (!ps || ps.tagName != 'BR') {
46630 if (!node.previousSibling) {
46633 var ps = node.previousSibling;
46635 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46636 ps = ps.previousSibling;
46638 if (!ps || ps.nodeType != 1) {
46641 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46642 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46646 node.parentNode.removeChild(node); // remove me...
46648 return false; // no need to do children
46655 * @class Roo.htmleditor.FilterBlock
46656 * removes id / data-block and contenteditable that are associated with blocks
46657 * usage should be done on a cloned copy of the dom
46659 * Run a new Attribute Filter { node : xxxx }}
46660 * @param {Object} config Configuration options
46662 Roo.htmleditor.FilterBlock = function(cfg)
46664 Roo.apply(this, cfg);
46665 var qa = cfg.node.querySelectorAll;
46666 this.removeAttributes('data-block');
46667 this.removeAttributes('contenteditable');
46668 this.removeAttributes('id');
46672 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
46674 node: true, // all tags
46677 removeAttributes : function(attr)
46679 var ar = this.node.querySelectorAll('*[' + attr + ']');
46680 for (var i =0;i<ar.length;i++) {
46681 ar[i].removeAttribute(attr);
46690 * This is based loosely on tinymce
46691 * @class Roo.htmleditor.TidySerializer
46692 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46694 * @method Serializer
46695 * @param {Object} settings Name/value settings object.
46699 Roo.htmleditor.TidySerializer = function(settings)
46701 Roo.apply(this, settings);
46703 this.writer = new Roo.htmleditor.TidyWriter(settings);
46708 Roo.htmleditor.TidySerializer.prototype = {
46711 * @param {boolean} inner do the inner of the node.
46718 * Serializes the specified node into a string.
46721 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
46722 * @method serialize
46723 * @param {DomElement} node Node instance to serialize.
46724 * @return {String} String with HTML based on DOM tree.
46726 serialize : function(node) {
46728 // = settings.validate;
46729 var writer = this.writer;
46733 3: function(node) {
46735 writer.text(node.nodeValue, node);
46738 8: function(node) {
46739 writer.comment(node.nodeValue);
46741 // Processing instruction
46742 7: function(node) {
46743 writer.pi(node.name, node.nodeValue);
46746 10: function(node) {
46747 writer.doctype(node.nodeValue);
46750 4: function(node) {
46751 writer.cdata(node.nodeValue);
46753 // Document fragment
46754 11: function(node) {
46755 node = node.firstChild;
46761 node = node.nextSibling
46766 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
46767 return writer.getContent();
46770 walk: function(node)
46772 var attrName, attrValue, sortedAttrs, i, l, elementRule,
46773 handler = this.handlers[node.nodeType];
46780 var name = node.nodeName;
46781 var isEmpty = node.childNodes.length < 1;
46783 var writer = this.writer;
46784 var attrs = node.attributes;
46787 writer.start(node.nodeName, attrs, isEmpty, node);
46791 node = node.firstChild;
46798 node = node.nextSibling;
46804 // Serialize element and treat all non elements as fragments
46809 * This is based loosely on tinymce
46810 * @class Roo.htmleditor.TidyWriter
46811 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46814 * - not tested much with 'PRE' formated elements.
46820 Roo.htmleditor.TidyWriter = function(settings)
46823 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
46824 Roo.apply(this, settings);
46828 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
46831 Roo.htmleditor.TidyWriter.prototype = {
46838 // part of state...
46842 last_inline : false,
46847 * Writes the a start element such as <p id="a">.
46850 * @param {String} name Name of the element.
46851 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
46852 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
46854 start: function(name, attrs, empty, node)
46856 var i, l, attr, value;
46858 // there are some situations where adding line break && indentation will not work. will not work.
46859 // <span / b / i ... formating?
46861 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46862 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
46864 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
46866 var add_lb = name == 'BR' ? false : in_inline;
46868 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
46872 var indentstr = this.indentstr;
46874 // e_inline = elements that can be inline, but still allow \n before and after?
46875 // only 'BR' ??? any others?
46877 // ADD LINE BEFORE tage
46878 if (!this.in_pre) {
46881 if (name == 'BR') {
46883 } else if (this.lastElementEndsWS()) {
46886 // otherwise - no new line. (and dont indent.)
46897 this.html.push(indentstr + '<', name.toLowerCase());
46900 for (i = 0, l = attrs.length; i < l; i++) {
46902 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
46908 this.html[this.html.length] = '/>';
46910 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
46912 var e_inline = name == 'BR' ? false : this.in_inline;
46914 if (!e_inline && !this.in_pre) {
46921 this.html[this.html.length] = '>';
46923 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
46925 if (!in_inline && !in_pre) {
46926 var cn = node.firstChild;
46928 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
46932 cn = cn.nextSibling;
46940 indentstr : in_pre ? '' : (this.indentstr + this.indent),
46942 in_inline : in_inline
46944 // add a line after if we are not in a
46946 if (!in_inline && !in_pre) {
46955 lastElementEndsWS : function()
46957 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
46958 if (value === false) {
46961 return value.match(/\s+$/);
46966 * Writes the a end element such as </p>.
46969 * @param {String} name Name of the element.
46971 end: function(name) {
46974 var indentstr = '';
46975 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46977 if (!this.in_pre && !in_inline) {
46979 indentstr = this.indentstr;
46981 this.html.push(indentstr + '</', name.toLowerCase(), '>');
46982 this.last_inline = in_inline;
46984 // pop the indent state..
46987 * Writes a text node.
46989 * In pre - we should not mess with the contents.
46993 * @param {String} text String to write out.
46994 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
46996 text: function(in_text, node)
46998 // if not in whitespace critical
46999 if (in_text.length < 1) {
47002 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
47005 this.html[this.html.length] = text;
47009 if (this.in_inline) {
47010 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
47012 text = text.replace(/\s+/,' '); // all white space to single white space
47015 // if next tag is '<BR>', then we can trim right..
47016 if (node.nextSibling &&
47017 node.nextSibling.nodeType == 1 &&
47018 node.nextSibling.nodeName == 'BR' )
47020 text = text.replace(/\s+$/g,'');
47022 // if previous tag was a BR, we can also trim..
47023 if (node.previousSibling &&
47024 node.previousSibling.nodeType == 1 &&
47025 node.previousSibling.nodeName == 'BR' )
47027 text = this.indentstr + text.replace(/^\s+/g,'');
47029 if (text.match(/\n/)) {
47030 text = text.replace(
47031 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
47033 // remoeve the last whitespace / line break.
47034 text = text.replace(/\n\s+$/,'');
47036 // repace long lines
47040 this.html[this.html.length] = text;
47043 // see if previous element was a inline element.
47044 var indentstr = this.indentstr;
47046 text = text.replace(/\s+/g," "); // all whitespace into single white space.
47048 // should trim left?
47049 if (node.previousSibling &&
47050 node.previousSibling.nodeType == 1 &&
47051 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
47057 text = text.replace(/^\s+/,''); // trim left
47060 // should trim right?
47061 if (node.nextSibling &&
47062 node.nextSibling.nodeType == 1 &&
47063 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
47068 text = text.replace(/\s+$/,''); // trim right
47075 if (text.length < 1) {
47078 if (!text.match(/\n/)) {
47079 this.html.push(indentstr + text);
47083 text = this.indentstr + text.replace(
47084 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
47086 // remoeve the last whitespace / line break.
47087 text = text.replace(/\s+$/,'');
47089 this.html.push(text);
47091 // split and indent..
47096 * Writes a cdata node such as <![CDATA[data]]>.
47099 * @param {String} text String to write out inside the cdata.
47101 cdata: function(text) {
47102 this.html.push('<![CDATA[', text, ']]>');
47105 * Writes a comment node such as <!-- Comment -->.
47108 * @param {String} text String to write out inside the comment.
47110 comment: function(text) {
47111 this.html.push('<!--', text, '-->');
47114 * Writes a PI node such as <?xml attr="value" ?>.
47117 * @param {String} name Name of the pi.
47118 * @param {String} text String to write out inside the pi.
47120 pi: function(name, text) {
47121 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
47122 this.indent != '' && this.html.push('\n');
47125 * Writes a doctype node such as <!DOCTYPE data>.
47128 * @param {String} text String to write out inside the doctype.
47130 doctype: function(text) {
47131 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
47134 * Resets the internal buffer if one wants to reuse the writer.
47138 reset: function() {
47139 this.html.length = 0;
47148 * Returns the contents that got serialized.
47150 * @method getContent
47151 * @return {String} HTML contents that got written down.
47153 getContent: function() {
47154 return this.html.join('').replace(/\n$/, '');
47157 pushState : function(cfg)
47159 this.state.push(cfg);
47160 Roo.apply(this, cfg);
47163 popState : function()
47165 if (this.state.length < 1) {
47166 return; // nothing to push
47173 if (this.state.length > 0) {
47174 cfg = this.state[this.state.length-1];
47176 Roo.apply(this, cfg);
47179 addLine: function()
47181 if (this.html.length < 1) {
47186 var value = this.html[this.html.length - 1];
47187 if (value.length > 0 && '\n' !== value) {
47188 this.html.push('\n');
47193 //'pre script noscript style textarea video audio iframe object code'
47194 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
47198 Roo.htmleditor.TidyWriter.inline_elements = [
47199 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
47200 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
47202 Roo.htmleditor.TidyWriter.shortend_elements = [
47203 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
47204 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
47207 Roo.htmleditor.TidyWriter.whitespace_elements = [
47208 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
47210 * This is based loosely on tinymce
47211 * @class Roo.htmleditor.TidyEntities
47213 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
47215 * Not 100% sure this is actually used or needed.
47218 Roo.htmleditor.TidyEntities = {
47221 * initialize data..
47223 init : function (){
47225 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
47230 buildEntitiesLookup: function(items, radix) {
47231 var i, chr, entity, lookup = {};
47235 items = typeof(items) == 'string' ? items.split(',') : items;
47236 radix = radix || 10;
47237 // Build entities lookup table
47238 for (i = 0; i < items.length; i += 2) {
47239 chr = String.fromCharCode(parseInt(items[i], radix));
47240 // Only add non base entities
47241 if (!this.baseEntities[chr]) {
47242 entity = '&' + items[i + 1] + ';';
47243 lookup[chr] = entity;
47244 lookup[entity] = chr;
47283 // Needs to be escaped since the YUI compressor would otherwise break the code
47290 // Reverse lookup table for raw entities
47291 reverseEntities : {
47299 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47300 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47301 rawCharsRegExp : /[<>&\"\']/g,
47302 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
47303 namedEntities : false,
47304 namedEntitiesData : [
47805 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
47807 * @method encodeRaw
47808 * @param {String} text Text to encode.
47809 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47810 * @return {String} Entity encoded text.
47812 encodeRaw: function(text, attr)
47815 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47816 return t.baseEntities[chr] || chr;
47820 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
47821 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
47822 * and is exposed as the DOMUtils.encode function.
47824 * @method encodeAllRaw
47825 * @param {String} text Text to encode.
47826 * @return {String} Entity encoded text.
47828 encodeAllRaw: function(text) {
47830 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
47831 return t.baseEntities[chr] || chr;
47835 * Encodes the specified string using numeric entities. The core entities will be
47836 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
47838 * @method encodeNumeric
47839 * @param {String} text Text to encode.
47840 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47841 * @return {String} Entity encoded text.
47843 encodeNumeric: function(text, attr) {
47845 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47846 // Multi byte sequence convert it to a single entity
47847 if (chr.length > 1) {
47848 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
47850 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
47854 * Encodes the specified string using named entities. The core entities will be encoded
47855 * as named ones but all non lower ascii characters will be encoded into named entities.
47857 * @method encodeNamed
47858 * @param {String} text Text to encode.
47859 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47860 * @param {Object} entities Optional parameter with entities to use.
47861 * @return {String} Entity encoded text.
47863 encodeNamed: function(text, attr, entities) {
47865 entities = entities || this.namedEntities;
47866 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47867 return t.baseEntities[chr] || entities[chr] || chr;
47871 * Returns an encode function based on the name(s) and it's optional entities.
47873 * @method getEncodeFunc
47874 * @param {String} name Comma separated list of encoders for example named,numeric.
47875 * @param {String} entities Optional parameter with entities to use instead of the built in set.
47876 * @return {function} Encode function to be used.
47878 getEncodeFunc: function(name, entities) {
47879 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
47881 function encodeNamedAndNumeric(text, attr) {
47882 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
47883 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
47887 function encodeCustomNamed(text, attr) {
47888 return t.encodeNamed(text, attr, entities);
47890 // Replace + with , to be compatible with previous TinyMCE versions
47891 name = this.makeMap(name.replace(/\+/g, ','));
47892 // Named and numeric encoder
47893 if (name.named && name.numeric) {
47894 return this.encodeNamedAndNumeric;
47900 return encodeCustomNamed;
47902 return this.encodeNamed;
47905 if (name.numeric) {
47906 return this.encodeNumeric;
47909 return this.encodeRaw;
47912 * Decodes the specified string, this will replace entities with raw UTF characters.
47915 * @param {String} text Text to entity decode.
47916 * @return {String} Entity decoded string.
47918 decode: function(text)
47921 return text.replace(this.entityRegExp, function(all, numeric) {
47923 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
47924 // Support upper UTF
47925 if (numeric > 65535) {
47927 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
47929 return t.asciiMap[numeric] || String.fromCharCode(numeric);
47931 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
47934 nativeDecode : function (text) {
47937 makeMap : function (items, delim, map) {
47939 items = items || [];
47940 delim = delim || ',';
47941 if (typeof items == "string") {
47942 items = items.split(delim);
47947 map[items[i]] = {};
47955 Roo.htmleditor.TidyEntities.init();
47957 * @class Roo.htmleditor.KeyEnter
47958 * Handle Enter press..
47959 * @cfg {Roo.HtmlEditorCore} core the editor.
47961 * Create a new Filter.
47962 * @param {Object} config Configuration options
47969 Roo.htmleditor.KeyEnter = function(cfg) {
47970 Roo.apply(this, cfg);
47971 // this does not actually call walk as it's really just a abstract class
47973 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
47976 //Roo.htmleditor.KeyEnter.i = 0;
47979 Roo.htmleditor.KeyEnter.prototype = {
47983 keypress : function(e)
47985 if (e.charCode != 13 && e.charCode != 10) {
47986 Roo.log([e.charCode,e]);
47989 e.preventDefault();
47990 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
47991 var doc = this.core.doc;
47995 var sel = this.core.getSelection();
47996 var range = sel.getRangeAt(0);
47997 var n = range.commonAncestorContainer;
47998 var pc = range.closest([ 'ol', 'ul']);
47999 var pli = range.closest('li');
48000 if (!pc || e.ctrlKey) {
48001 // on it list, or ctrl pressed.
48003 sel.insertNode('br', 'after');
48005 // only do this if we have ctrl key..
48006 var br = doc.createElement('br');
48007 br.className = 'clear';
48008 br.setAttribute('style', 'clear: both');
48009 sel.insertNode(br, 'after');
48013 this.core.undoManager.addEvent();
48014 this.core.fireEditorEvent(e);
48018 // deal with <li> insetion
48019 if (pli.innerText.trim() == '' &&
48020 pli.previousSibling &&
48021 pli.previousSibling.nodeName == 'LI' &&
48022 pli.previousSibling.innerText.trim() == '') {
48023 pli.parentNode.removeChild(pli.previousSibling);
48024 sel.cursorAfter(pc);
48025 this.core.undoManager.addEvent();
48026 this.core.fireEditorEvent(e);
48030 var li = doc.createElement('LI');
48031 li.innerHTML = ' ';
48032 if (!pli || !pli.firstSibling) {
48033 pc.appendChild(li);
48035 pli.parentNode.insertBefore(li, pli.firstSibling);
48037 sel.cursorText (li.firstChild);
48039 this.core.undoManager.addEvent();
48040 this.core.fireEditorEvent(e);
48052 * @class Roo.htmleditor.Block
48053 * Base class for html editor blocks - do not use it directly .. extend it..
48054 * @cfg {DomElement} node The node to apply stuff to.
48055 * @cfg {String} friendly_name the name that appears in the context bar about this block
48056 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
48059 * Create a new Filter.
48060 * @param {Object} config Configuration options
48063 Roo.htmleditor.Block = function(cfg)
48065 // do nothing .. should not be called really.
48068 * factory method to get the block from an element (using cache if necessary)
48070 * @param {HtmlElement} the dom element
48072 Roo.htmleditor.Block.factory = function(node)
48074 var cc = Roo.htmleditor.Block.cache;
48075 var id = Roo.get(node).id;
48076 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
48077 Roo.htmleditor.Block.cache[id].readElement(node);
48078 return Roo.htmleditor.Block.cache[id];
48080 var db = node.getAttribute('data-block');
48082 db = node.nodeName.toLowerCase().toUpperCaseFirst();
48084 var cls = Roo.htmleditor['Block' + db];
48085 if (typeof(cls) == 'undefined') {
48086 //Roo.log(node.getAttribute('data-block'));
48087 Roo.log("OOps missing block : " + 'Block' + db);
48090 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
48091 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
48095 * initalize all Elements from content that are 'blockable'
48097 * @param the body element
48099 Roo.htmleditor.Block.initAll = function(body, type)
48101 if (typeof(type) == 'undefined') {
48102 var ia = Roo.htmleditor.Block.initAll;
48108 Roo.each(Roo.get(body).query(type), function(e) {
48109 Roo.htmleditor.Block.factory(e);
48112 // question goes here... do we need to clear out this cache sometimes?
48113 // or show we make it relivant to the htmleditor.
48114 Roo.htmleditor.Block.cache = {};
48116 Roo.htmleditor.Block.prototype = {
48120 // used by context menu
48121 friendly_name : 'Based Block',
48123 // text for button to delete this element
48124 deleteTitle : false,
48128 * Update a node with values from this object
48129 * @param {DomElement} node
48131 updateElement : function(node)
48133 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
48136 * convert to plain HTML for calling insertAtCursor..
48138 toHTML : function()
48140 return Roo.DomHelper.markup(this.toObject());
48143 * used by readEleemnt to extract data from a node
48144 * may need improving as it's pretty basic
48146 * @param {DomElement} node
48147 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
48148 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
48149 * @param {String} style the style property - eg. text-align
48151 getVal : function(node, tag, attr, style)
48154 if (tag !== true && n.tagName != tag.toUpperCase()) {
48155 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
48156 // but kiss for now.
48157 n = node.getElementsByTagName(tag).item(0);
48162 if (attr === false) {
48165 if (attr == 'html') {
48166 return n.innerHTML;
48168 if (attr == 'style') {
48169 return n.style[style];
48172 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
48176 * create a DomHelper friendly object - for use with
48177 * Roo.DomHelper.markup / overwrite / etc..
48180 toObject : function()
48185 * Read a node that has a 'data-block' property - and extract the values from it.
48186 * @param {DomElement} node - the node
48188 readElement : function(node)
48199 * @class Roo.htmleditor.BlockFigure
48200 * Block that has an image and a figcaption
48201 * @cfg {String} image_src the url for the image
48202 * @cfg {String} align (left|right) alignment for the block default left
48203 * @cfg {String} caption the text to appear below (and in the alt tag)
48204 * @cfg {String} caption_display (block|none) display or not the caption
48205 * @cfg {String|number} image_width the width of the image number or %?
48206 * @cfg {String|number} image_height the height of the image number or %?
48209 * Create a new Filter.
48210 * @param {Object} config Configuration options
48213 Roo.htmleditor.BlockFigure = function(cfg)
48216 this.readElement(cfg.node);
48217 this.updateElement(cfg.node);
48219 Roo.apply(this, cfg);
48221 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
48228 caption_display : 'block',
48234 // margin: '2%', not used
48236 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
48239 // used by context menu
48240 friendly_name : 'Image with caption',
48241 deleteTitle : "Delete Image and Caption",
48243 contextMenu : function(toolbar)
48246 var block = function() {
48247 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48251 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48253 var syncValue = toolbar.editorcore.syncValue;
48259 xtype : 'TextItem',
48261 xns : rooui.Toolbar //Boostrap?
48265 text: 'Change Image URL',
48268 click: function (btn, state)
48272 Roo.MessageBox.show({
48273 title : "Image Source URL",
48274 msg : "Enter the url for the image",
48275 buttons: Roo.MessageBox.OKCANCEL,
48276 fn: function(btn, val){
48283 toolbar.editorcore.onEditorEvent();
48287 //multiline: multiline,
48289 value : b.image_src
48293 xns : rooui.Toolbar
48298 text: 'Change Link URL',
48301 click: function (btn, state)
48305 Roo.MessageBox.show({
48306 title : "Link URL",
48307 msg : "Enter the url for the link - leave blank to have no link",
48308 buttons: Roo.MessageBox.OKCANCEL,
48309 fn: function(btn, val){
48316 toolbar.editorcore.onEditorEvent();
48320 //multiline: multiline,
48326 xns : rooui.Toolbar
48330 text: 'Show Video URL',
48333 click: function (btn, state)
48335 Roo.MessageBox.alert("Video URL",
48336 block().video_url == '' ? 'This image is not linked ot a video' :
48337 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
48340 xns : rooui.Toolbar
48345 xtype : 'TextItem',
48347 xns : rooui.Toolbar //Boostrap?
48350 xtype : 'ComboBox',
48351 allowBlank : false,
48352 displayField : 'val',
48355 triggerAction : 'all',
48357 valueField : 'val',
48361 select : function (combo, r, index)
48363 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48365 b.width = r.get('val');
48368 toolbar.editorcore.onEditorEvent();
48373 xtype : 'SimpleStore',
48386 xtype : 'TextItem',
48388 xns : rooui.Toolbar //Boostrap?
48391 xtype : 'ComboBox',
48392 allowBlank : false,
48393 displayField : 'val',
48396 triggerAction : 'all',
48398 valueField : 'val',
48402 select : function (combo, r, index)
48404 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48406 b.align = r.get('val');
48409 toolbar.editorcore.onEditorEvent();
48414 xtype : 'SimpleStore',
48428 text: 'Hide Caption',
48429 name : 'caption_display',
48431 enableToggle : true,
48432 setValue : function(v) {
48433 // this trigger toggle.
48435 this.setText(v ? "Hide Caption" : "Show Caption");
48436 this.setPressed(v != 'block');
48439 toggle: function (btn, state)
48442 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
48443 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
48446 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48447 toolbar.editorcore.onEditorEvent();
48450 xns : rooui.Toolbar
48456 * create a DomHelper friendly object - for use with
48457 * Roo.DomHelper.markup / overwrite / etc..
48459 toObject : function()
48461 var d = document.createElement('div');
48462 d.innerHTML = this.caption;
48464 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
48466 var iw = this.align == 'center' ? this.width : '100%';
48469 contenteditable : 'false',
48470 src : this.image_src,
48471 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
48474 maxWidth : iw + ' !important', // this is not getting rendered?
48480 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
48482 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
48487 if (this.href.length > 0) {
48491 contenteditable : 'true',
48499 if (this.video_url.length > 0) {
48504 allowfullscreen : true,
48505 width : 420, // these are for video tricks - that we replace the outer
48507 src : this.video_url,
48513 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
48514 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
48519 'data-block' : 'Figure',
48520 'data-width' : this.width,
48521 contenteditable : 'false',
48525 float : this.align ,
48526 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
48527 width : this.align == 'center' ? '100%' : this.width,
48529 padding: this.align == 'center' ? '0' : '0 10px' ,
48530 textAlign : this.align // seems to work for email..
48535 align : this.align,
48541 'data-display' : this.caption_display,
48543 textAlign : 'left',
48545 lineHeight : '24px',
48546 display : this.caption_display,
48547 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
48549 width: this.align == 'center' ? this.width : '100%'
48553 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
48558 marginTop : '16px',
48564 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
48566 contenteditable : true,
48582 readElement : function(node)
48584 // this should not really come from the link...
48585 this.video_url = this.getVal(node, 'div', 'src');
48586 this.cls = this.getVal(node, 'div', 'class');
48587 this.href = this.getVal(node, 'a', 'href');
48590 this.image_src = this.getVal(node, 'img', 'src');
48592 this.align = this.getVal(node, 'figure', 'align');
48593 var figcaption = this.getVal(node, 'figcaption', false);
48594 if (figcaption !== '') {
48595 this.caption = this.getVal(figcaption, 'i', 'html');
48599 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
48600 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
48601 this.width = this.getVal(node, true, 'data-width');
48602 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
48605 removeNode : function()
48622 * @class Roo.htmleditor.BlockTable
48623 * Block that manages a table
48626 * Create a new Filter.
48627 * @param {Object} config Configuration options
48630 Roo.htmleditor.BlockTable = function(cfg)
48633 this.readElement(cfg.node);
48634 this.updateElement(cfg.node);
48636 Roo.apply(this, cfg);
48639 for(var r = 0; r < this.no_row; r++) {
48641 for(var c = 0; c < this.no_col; c++) {
48642 this.rows[r][c] = this.emptyCell();
48649 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
48658 // used by context menu
48659 friendly_name : 'Table',
48660 deleteTitle : 'Delete Table',
48661 // context menu is drawn once..
48663 contextMenu : function(toolbar)
48666 var block = function() {
48667 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48671 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48673 var syncValue = toolbar.editorcore.syncValue;
48679 xtype : 'TextItem',
48681 xns : rooui.Toolbar //Boostrap?
48684 xtype : 'ComboBox',
48685 allowBlank : false,
48686 displayField : 'val',
48689 triggerAction : 'all',
48691 valueField : 'val',
48695 select : function (combo, r, index)
48697 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48699 b.width = r.get('val');
48702 toolbar.editorcore.onEditorEvent();
48707 xtype : 'SimpleStore',
48719 xtype : 'TextItem',
48720 text : "Columns: ",
48721 xns : rooui.Toolbar //Boostrap?
48728 click : function (_self, e)
48730 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48731 block().removeColumn();
48733 toolbar.editorcore.onEditorEvent();
48736 xns : rooui.Toolbar
48742 click : function (_self, e)
48744 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48745 block().addColumn();
48747 toolbar.editorcore.onEditorEvent();
48750 xns : rooui.Toolbar
48754 xtype : 'TextItem',
48756 xns : rooui.Toolbar //Boostrap?
48763 click : function (_self, e)
48765 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48766 block().removeRow();
48768 toolbar.editorcore.onEditorEvent();
48771 xns : rooui.Toolbar
48777 click : function (_self, e)
48781 toolbar.editorcore.onEditorEvent();
48784 xns : rooui.Toolbar
48789 text: 'Reset Column Widths',
48792 click : function (_self, e)
48794 block().resetWidths();
48796 toolbar.editorcore.onEditorEvent();
48799 xns : rooui.Toolbar
48810 * create a DomHelper friendly object - for use with
48811 * Roo.DomHelper.markup / overwrite / etc..
48812 * ?? should it be called with option to hide all editing features?
48814 toObject : function()
48819 contenteditable : 'false', // this stops cell selection from picking the table.
48820 'data-block' : 'Table',
48823 border : 'solid 1px #000', // ??? hard coded?
48824 'border-collapse' : 'collapse'
48827 { tag : 'tbody' , cn : [] }
48831 // do we have a head = not really
48833 Roo.each(this.rows, function( row ) {
48838 border : 'solid 1px #000',
48844 ret.cn[0].cn.push(tr);
48845 // does the row have any properties? ?? height?
48847 Roo.each(row, function( cell ) {
48851 contenteditable : 'true',
48852 'data-block' : 'Td',
48856 if (cell.colspan > 1) {
48857 td.colspan = cell.colspan ;
48858 nc += cell.colspan;
48862 if (cell.rowspan > 1) {
48863 td.rowspan = cell.rowspan ;
48872 ncols = Math.max(nc, ncols);
48876 // add the header row..
48885 readElement : function(node)
48887 node = node ? node : this.node ;
48888 this.width = this.getVal(node, true, 'style', 'width') || '100%';
48892 var trs = Array.from(node.rows);
48893 trs.forEach(function(tr) {
48895 this.rows.push(row);
48899 Array.from(tr.cells).forEach(function(td) {
48902 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
48903 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
48904 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
48905 html : td.innerHTML
48907 no_column += add.colspan;
48914 this.no_col = Math.max(this.no_col, no_column);
48921 normalizeRows: function()
48925 this.rows.forEach(function(row) {
48928 row = this.normalizeRow(row);
48930 row.forEach(function(c) {
48931 while (typeof(ret[rid][cid]) != 'undefined') {
48934 if (typeof(ret[rid]) == 'undefined') {
48940 if (c.rowspan < 2) {
48944 for(var i = 1 ;i < c.rowspan; i++) {
48945 if (typeof(ret[rid+i]) == 'undefined') {
48948 ret[rid+i][cid] = c;
48956 normalizeRow: function(row)
48959 row.forEach(function(c) {
48960 if (c.colspan < 2) {
48964 for(var i =0 ;i < c.colspan; i++) {
48972 deleteColumn : function(sel)
48974 if (!sel || sel.type != 'col') {
48977 if (this.no_col < 2) {
48981 this.rows.forEach(function(row) {
48982 var cols = this.normalizeRow(row);
48983 var col = cols[sel.col];
48984 if (col.colspan > 1) {
48994 removeColumn : function()
48996 this.deleteColumn({
48998 col : this.no_col-1
49000 this.updateElement();
49004 addColumn : function()
49007 this.rows.forEach(function(row) {
49008 row.push(this.emptyCell());
49011 this.updateElement();
49014 deleteRow : function(sel)
49016 if (!sel || sel.type != 'row') {
49020 if (this.no_row < 2) {
49024 var rows = this.normalizeRows();
49027 rows[sel.row].forEach(function(col) {
49028 if (col.rowspan > 1) {
49031 col.remove = 1; // flage it as removed.
49036 this.rows.forEach(function(row) {
49038 row.forEach(function(c) {
49039 if (typeof(c.remove) == 'undefined') {
49044 if (newrow.length > 0) {
49048 this.rows = newrows;
49053 this.updateElement();
49056 removeRow : function()
49060 row : this.no_row-1
49066 addRow : function()
49070 for (var i = 0; i < this.no_col; i++ ) {
49072 row.push(this.emptyCell());
49075 this.rows.push(row);
49076 this.updateElement();
49080 // the default cell object... at present...
49081 emptyCell : function() {
49082 return (new Roo.htmleditor.BlockTd({})).toObject();
49087 removeNode : function()
49094 resetWidths : function()
49096 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
49097 var nn = Roo.htmleditor.Block.factory(n);
49099 nn.updateElement(n);
49112 * since selections really work on the table cell, then editing really should work from there
49114 * The original plan was to support merging etc... - but that may not be needed yet..
49116 * So this simple version will support:
49118 * adjust the width +/-
49119 * reset the width...
49128 * @class Roo.htmleditor.BlockTable
49129 * Block that manages a table
49132 * Create a new Filter.
49133 * @param {Object} config Configuration options
49136 Roo.htmleditor.BlockTd = function(cfg)
49139 this.readElement(cfg.node);
49140 this.updateElement(cfg.node);
49142 Roo.apply(this, cfg);
49147 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
49152 textAlign : 'left',
49159 // used by context menu
49160 friendly_name : 'Table Cell',
49161 deleteTitle : false, // use our customer delete
49163 // context menu is drawn once..
49165 contextMenu : function(toolbar)
49168 var cell = function() {
49169 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
49172 var table = function() {
49173 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
49177 var saveSel = function()
49179 lr = toolbar.editorcore.getSelection().getRangeAt(0);
49181 var restoreSel = function()
49185 toolbar.editorcore.focus();
49186 var cr = toolbar.editorcore.getSelection();
49187 cr.removeAllRanges();
49189 toolbar.editorcore.onEditorEvent();
49190 }).defer(10, this);
49196 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
49198 var syncValue = toolbar.editorcore.syncValue;
49205 text : 'Edit Table',
49207 click : function() {
49208 var t = toolbar.tb.selectedNode.closest('table');
49209 toolbar.editorcore.selectNode(t);
49210 toolbar.editorcore.onEditorEvent();
49219 xtype : 'TextItem',
49220 text : "Column Width: ",
49221 xns : rooui.Toolbar
49228 click : function (_self, e)
49230 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49231 cell().shrinkColumn();
49233 toolbar.editorcore.onEditorEvent();
49236 xns : rooui.Toolbar
49242 click : function (_self, e)
49244 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49245 cell().growColumn();
49247 toolbar.editorcore.onEditorEvent();
49250 xns : rooui.Toolbar
49254 xtype : 'TextItem',
49255 text : "Vertical Align: ",
49256 xns : rooui.Toolbar //Boostrap?
49259 xtype : 'ComboBox',
49260 allowBlank : false,
49261 displayField : 'val',
49264 triggerAction : 'all',
49266 valueField : 'val',
49270 select : function (combo, r, index)
49272 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49274 b.valign = r.get('val');
49277 toolbar.editorcore.onEditorEvent();
49282 xtype : 'SimpleStore',
49286 ['bottom'] // there are afew more...
49294 xtype : 'TextItem',
49295 text : "Merge Cells: ",
49296 xns : rooui.Toolbar
49305 click : function (_self, e)
49307 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49308 cell().mergeRight();
49309 //block().growColumn();
49311 toolbar.editorcore.onEditorEvent();
49314 xns : rooui.Toolbar
49321 click : function (_self, e)
49323 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49324 cell().mergeBelow();
49325 //block().growColumn();
49327 toolbar.editorcore.onEditorEvent();
49330 xns : rooui.Toolbar
49333 xtype : 'TextItem',
49335 xns : rooui.Toolbar
49343 click : function (_self, e)
49345 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49348 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49349 toolbar.editorcore.onEditorEvent();
49353 xns : rooui.Toolbar
49357 xns : rooui.Toolbar
49366 xns : rooui.Toolbar,
49375 click : function (_self, e)
49379 cell().deleteColumn();
49381 toolbar.editorcore.selectNode(t.node);
49382 toolbar.editorcore.onEditorEvent();
49391 click : function (_self, e)
49394 cell().deleteRow();
49397 toolbar.editorcore.selectNode(t.node);
49398 toolbar.editorcore.onEditorEvent();
49405 xtype : 'Separator',
49412 click : function (_self, e)
49415 var nn = t.node.nextSibling || t.node.previousSibling;
49416 t.node.parentNode.removeChild(t.node);
49418 toolbar.editorcore.selectNode(nn, true);
49420 toolbar.editorcore.onEditorEvent();
49430 // align... << fixme
49438 * create a DomHelper friendly object - for use with
49439 * Roo.DomHelper.markup / overwrite / etc..
49440 * ?? should it be called with option to hide all editing features?
49443 * create a DomHelper friendly object - for use with
49444 * Roo.DomHelper.markup / overwrite / etc..
49445 * ?? should it be called with option to hide all editing features?
49447 toObject : function()
49451 contenteditable : 'true', // this stops cell selection from picking the table.
49452 'data-block' : 'Td',
49453 valign : this.valign,
49455 'text-align' : this.textAlign,
49456 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
49457 'border-collapse' : 'collapse',
49458 padding : '6px', // 8 for desktop / 4 for mobile
49459 'vertical-align': this.valign
49463 if (this.width != '') {
49464 ret.width = this.width;
49465 ret.style.width = this.width;
49469 if (this.colspan > 1) {
49470 ret.colspan = this.colspan ;
49472 if (this.rowspan > 1) {
49473 ret.rowspan = this.rowspan ;
49482 readElement : function(node)
49484 node = node ? node : this.node ;
49485 this.width = node.style.width;
49486 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
49487 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
49488 this.html = node.innerHTML;
49489 if (node.style.textAlign != '') {
49490 this.textAlign = node.style.textAlign;
49496 // the default cell object... at present...
49497 emptyCell : function() {
49501 textAlign : 'left',
49502 html : " " // is this going to be editable now?
49507 removeNode : function()
49509 return this.node.closest('table');
49517 toTableArray : function()
49520 var tab = this.node.closest('tr').closest('table');
49521 Array.from(tab.rows).forEach(function(r, ri){
49525 this.colWidths = [];
49526 var all_auto = true;
49527 Array.from(tab.rows).forEach(function(r, ri){
49530 Array.from(r.cells).forEach(function(ce, ci){
49535 colspan : ce.colSpan,
49536 rowspan : ce.rowSpan
49538 if (ce.isEqualNode(this.node)) {
49541 // if we have been filled up by a row?
49542 if (typeof(ret[rn][cn]) != 'undefined') {
49543 while(typeof(ret[rn][cn]) != 'undefined') {
49549 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
49550 this.colWidths[cn] = ce.style.width;
49551 if (this.colWidths[cn] != '') {
49557 if (c.colspan < 2 && c.rowspan < 2 ) {
49562 for(var j = 0; j < c.rowspan; j++) {
49563 if (typeof(ret[rn+j]) == 'undefined') {
49564 continue; // we have a problem..
49567 for(var i = 0; i < c.colspan; i++) {
49568 ret[rn+j][cn+i] = c;
49577 // initalize widths.?
49578 // either all widths or no widths..
49580 this.colWidths[0] = false; // no widths flag.
49591 mergeRight: function()
49594 // get the contents of the next cell along..
49595 var tr = this.node.closest('tr');
49596 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
49597 if (i >= tr.childNodes.length - 1) {
49598 return; // no cells on right to merge with.
49600 var table = this.toTableArray();
49602 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
49603 return; // nothing right?
49605 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
49606 // right cell - must be same rowspan and on the same row.
49607 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
49608 return; // right hand side is not same rowspan.
49613 this.node.innerHTML += ' ' + rc.cell.innerHTML;
49614 tr.removeChild(rc.cell);
49615 this.colspan += rc.colspan;
49616 this.node.setAttribute('colspan', this.colspan);
49618 var table = this.toTableArray();
49619 this.normalizeWidths(table);
49620 this.updateWidths(table);
49624 mergeBelow : function()
49626 var table = this.toTableArray();
49627 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
49628 return; // no row below
49630 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
49631 return; // nothing right?
49633 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
49635 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
49636 return; // right hand side is not same rowspan.
49638 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
49639 rc.cell.parentNode.removeChild(rc.cell);
49640 this.rowspan += rc.rowspan;
49641 this.node.setAttribute('rowspan', this.rowspan);
49646 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
49649 var table = this.toTableArray();
49650 var cd = this.cellData;
49654 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
49657 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
49658 if (r == cd.row && c == cd.col) {
49659 this.node.removeAttribute('rowspan');
49660 this.node.removeAttribute('colspan');
49663 var ntd = this.node.cloneNode(); // which col/row should be 0..
49664 ntd.removeAttribute('id');
49665 ntd.style.width = this.colWidths[c];
49666 ntd.innerHTML = '';
49667 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
49671 this.redrawAllCells(table);
49677 redrawAllCells: function(table)
49681 var tab = this.node.closest('tr').closest('table');
49682 var ctr = tab.rows[0].parentNode;
49683 Array.from(tab.rows).forEach(function(r, ri){
49685 Array.from(r.cells).forEach(function(ce, ci){
49686 ce.parentNode.removeChild(ce);
49688 r.parentNode.removeChild(r);
49690 for(var r = 0 ; r < table.length; r++) {
49691 var re = tab.rows[r];
49693 var re = tab.ownerDocument.createElement('tr');
49694 ctr.appendChild(re);
49695 for(var c = 0 ; c < table[r].length; c++) {
49696 if (table[r][c].cell === false) {
49700 re.appendChild(table[r][c].cell);
49702 table[r][c].cell = false;
49707 updateWidths : function(table)
49709 for(var r = 0 ; r < table.length; r++) {
49711 for(var c = 0 ; c < table[r].length; c++) {
49712 if (table[r][c].cell === false) {
49716 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
49717 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49718 el.width = Math.floor(this.colWidths[c]) +'%';
49719 el.updateElement(el.node);
49721 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
49722 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49724 for(var i = 0; i < table[r][c].colspan; i ++) {
49725 width += Math.floor(this.colWidths[c + i]);
49727 el.width = width +'%';
49728 el.updateElement(el.node);
49730 table[r][c].cell = false; // done
49734 normalizeWidths : function(table)
49736 if (this.colWidths[0] === false) {
49737 var nw = 100.0 / this.colWidths.length;
49738 this.colWidths.forEach(function(w,i) {
49739 this.colWidths[i] = nw;
49744 var t = 0, missing = [];
49746 this.colWidths.forEach(function(w,i) {
49748 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
49749 var add = this.colWidths[i];
49758 var nc = this.colWidths.length;
49759 if (missing.length) {
49760 var mult = (nc - missing.length) / (1.0 * nc);
49762 var ew = (100 -t) / (1.0 * missing.length);
49763 this.colWidths.forEach(function(w,i) {
49765 this.colWidths[i] = w * mult;
49769 this.colWidths[i] = ew;
49771 // have to make up numbers..
49774 // now we should have all the widths..
49779 shrinkColumn : function()
49781 var table = this.toTableArray();
49782 this.normalizeWidths(table);
49783 var col = this.cellData.col;
49784 var nw = this.colWidths[col] * 0.8;
49788 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
49789 this.colWidths.forEach(function(w,i) {
49791 this.colWidths[i] = nw;
49794 this.colWidths[i] += otherAdd
49796 this.updateWidths(table);
49799 growColumn : function()
49801 var table = this.toTableArray();
49802 this.normalizeWidths(table);
49803 var col = this.cellData.col;
49804 var nw = this.colWidths[col] * 1.2;
49808 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
49809 this.colWidths.forEach(function(w,i) {
49811 this.colWidths[i] = nw;
49814 this.colWidths[i] -= otherSub
49816 this.updateWidths(table);
49819 deleteRow : function()
49821 // delete this rows 'tr'
49822 // if any of the cells in this row have a rowspan > 1 && row!= this row..
49823 // then reduce the rowspan.
49824 var table = this.toTableArray();
49825 // this.cellData.row;
49826 for (var i =0;i< table[this.cellData.row].length ; i++) {
49827 var c = table[this.cellData.row][i];
49828 if (c.row != this.cellData.row) {
49831 c.cell.setAttribute('rowspan', c.rowspan);
49834 if (c.rowspan > 1) {
49836 c.cell.setAttribute('rowspan', c.rowspan);
49839 table.splice(this.cellData.row,1);
49840 this.redrawAllCells(table);
49843 deleteColumn : function()
49845 var table = this.toTableArray();
49847 for (var i =0;i< table.length ; i++) {
49848 var c = table[i][this.cellData.col];
49849 if (c.col != this.cellData.col) {
49850 table[i][this.cellData.col].colspan--;
49851 } else if (c.colspan > 1) {
49853 c.cell.setAttribute('colspan', c.colspan);
49855 table[i].splice(this.cellData.col,1);
49858 this.redrawAllCells(table);
49866 //<script type="text/javascript">
49869 * Based Ext JS Library 1.1.1
49870 * Copyright(c) 2006-2007, Ext JS, LLC.
49876 * @class Roo.HtmlEditorCore
49877 * @extends Roo.Component
49878 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
49880 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
49883 Roo.HtmlEditorCore = function(config){
49886 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
49891 * @event initialize
49892 * Fires when the editor is fully initialized (including the iframe)
49893 * @param {Roo.HtmlEditorCore} this
49898 * Fires when the editor is first receives the focus. Any insertion must wait
49899 * until after this event.
49900 * @param {Roo.HtmlEditorCore} this
49904 * @event beforesync
49905 * Fires before the textarea is updated with content from the editor iframe. Return false
49906 * to cancel the sync.
49907 * @param {Roo.HtmlEditorCore} this
49908 * @param {String} html
49912 * @event beforepush
49913 * Fires before the iframe editor is updated with content from the textarea. Return false
49914 * to cancel the push.
49915 * @param {Roo.HtmlEditorCore} this
49916 * @param {String} html
49921 * Fires when the textarea is updated with content from the editor iframe.
49922 * @param {Roo.HtmlEditorCore} this
49923 * @param {String} html
49928 * Fires when the iframe editor is updated with content from the textarea.
49929 * @param {Roo.HtmlEditorCore} this
49930 * @param {String} html
49935 * @event editorevent
49936 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
49937 * @param {Roo.HtmlEditorCore} this
49944 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
49946 // defaults : white / black...
49947 this.applyBlacklists();
49954 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
49958 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
49964 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
49969 * @cfg {Number} height (in pixels)
49973 * @cfg {Number} width (in pixels)
49977 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
49978 * if you are doing an email editor, this probably needs disabling, it's designed
49983 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
49985 enableBlocks : true,
49987 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
49990 stylesheets: false,
49992 * @cfg {String} language default en - language of text (usefull for rtl languages)
49998 * @cfg {boolean} allowComments - default false - allow comments in HTML source
49999 * - by default they are stripped - if you are editing email you may need this.
50001 allowComments: false,
50005 // private properties
50006 validationEvent : false,
50008 initialized : false,
50010 sourceEditMode : false,
50011 onFocus : Roo.emptyFn,
50013 hideMode:'offsets',
50017 // blacklist + whitelisted elements..
50024 undoManager : false,
50026 * Protected method that will not generally be called directly. It
50027 * is called when the editor initializes the iframe with HTML contents. Override this method if you
50028 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
50030 getDocMarkup : function(){
50034 // inherit styels from page...??
50035 if (this.stylesheets === false) {
50037 Roo.get(document.head).select('style').each(function(node) {
50038 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
50041 Roo.get(document.head).select('link').each(function(node) {
50042 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
50045 } else if (!this.stylesheets.length) {
50047 st = '<style type="text/css">' +
50048 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
50051 for (var i in this.stylesheets) {
50052 if (typeof(this.stylesheets[i]) != 'string') {
50055 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
50060 st += '<style type="text/css">' +
50061 'IMG { cursor: pointer } ' +
50064 st += '<meta name="google" content="notranslate">';
50066 var cls = 'notranslate roo-htmleditor-body';
50068 if(this.bodyCls.length){
50069 cls += ' ' + this.bodyCls;
50072 return '<html class="notranslate" translate="no"><head>' + st +
50073 //<style type="text/css">' +
50074 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
50076 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
50080 onRender : function(ct, position)
50083 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
50084 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
50087 this.el.dom.style.border = '0 none';
50088 this.el.dom.setAttribute('tabIndex', -1);
50089 this.el.addClass('x-hidden hide');
50093 if(Roo.isIE){ // fix IE 1px bogus margin
50094 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
50098 this.frameId = Roo.id();
50102 var iframe = this.owner.wrap.createChild({
50104 cls: 'form-control', // bootstrap..
50106 name: this.frameId,
50107 frameBorder : 'no',
50108 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
50113 this.iframe = iframe.dom;
50115 this.assignDocWin();
50117 this.doc.designMode = 'on';
50120 this.doc.write(this.getDocMarkup());
50124 var task = { // must defer to wait for browser to be ready
50126 //console.log("run task?" + this.doc.readyState);
50127 this.assignDocWin();
50128 if(this.doc.body || this.doc.readyState == 'complete'){
50130 this.doc.designMode="on";
50135 Roo.TaskMgr.stop(task);
50136 this.initEditor.defer(10, this);
50143 Roo.TaskMgr.start(task);
50148 onResize : function(w, h)
50150 Roo.log('resize: ' +w + ',' + h );
50151 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
50155 if(typeof w == 'number'){
50157 this.iframe.style.width = w + 'px';
50159 if(typeof h == 'number'){
50161 this.iframe.style.height = h + 'px';
50163 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
50170 * Toggles the editor between standard and source edit mode.
50171 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
50173 toggleSourceEdit : function(sourceEditMode){
50175 this.sourceEditMode = sourceEditMode === true;
50177 if(this.sourceEditMode){
50179 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
50182 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
50183 //this.iframe.className = '';
50186 //this.setSize(this.owner.wrap.getSize());
50187 //this.fireEvent('editmodechange', this, this.sourceEditMode);
50194 * Protected method that will not generally be called directly. If you need/want
50195 * custom HTML cleanup, this is the method you should override.
50196 * @param {String} html The HTML to be cleaned
50197 * return {String} The cleaned HTML
50199 cleanHtml : function(html)
50201 html = String(html);
50202 if(html.length > 5){
50203 if(Roo.isSafari){ // strip safari nonsense
50204 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
50207 if(html == ' '){
50214 * HTML Editor -> Textarea
50215 * Protected method that will not generally be called directly. Syncs the contents
50216 * of the editor iframe with the textarea.
50218 syncValue : function()
50220 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
50221 if(this.initialized){
50223 if (this.undoManager) {
50224 this.undoManager.addEvent();
50228 var bd = (this.doc.body || this.doc.documentElement);
50231 var sel = this.win.getSelection();
50233 var div = document.createElement('div');
50234 div.innerHTML = bd.innerHTML;
50235 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
50236 if (gtx.length > 0) {
50237 var rm = gtx.item(0).parentNode;
50238 rm.parentNode.removeChild(rm);
50242 if (this.enableBlocks) {
50243 new Roo.htmleditor.FilterBlock({ node : div });
50246 var html = div.innerHTML;
50249 if (this.autoClean) {
50251 new Roo.htmleditor.FilterAttributes({
50272 attrib_clean : ['href', 'src' ]
50275 var tidy = new Roo.htmleditor.TidySerializer({
50278 html = tidy.serialize(div);
50284 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
50285 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
50287 html = '<div style="'+m[0]+'">' + html + '</div>';
50290 html = this.cleanHtml(html);
50291 // fix up the special chars.. normaly like back quotes in word...
50292 // however we do not want to do this with chinese..
50293 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
50295 var cc = match.charCodeAt();
50297 // Get the character value, handling surrogate pairs
50298 if (match.length == 2) {
50299 // It's a surrogate pair, calculate the Unicode code point
50300 var high = match.charCodeAt(0) - 0xD800;
50301 var low = match.charCodeAt(1) - 0xDC00;
50302 cc = (high * 0x400) + low + 0x10000;
50304 (cc >= 0x4E00 && cc < 0xA000 ) ||
50305 (cc >= 0x3400 && cc < 0x4E00 ) ||
50306 (cc >= 0xf900 && cc < 0xfb00 )
50311 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
50312 return "&#" + cc + ";";
50319 if(this.owner.fireEvent('beforesync', this, html) !== false){
50320 this.el.dom.value = html;
50321 this.owner.fireEvent('sync', this, html);
50327 * TEXTAREA -> EDITABLE
50328 * Protected method that will not generally be called directly. Pushes the value of the textarea
50329 * into the iframe editor.
50331 pushValue : function()
50333 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
50334 if(this.initialized){
50335 var v = this.el.dom.value.trim();
50338 if(this.owner.fireEvent('beforepush', this, v) !== false){
50339 var d = (this.doc.body || this.doc.documentElement);
50342 this.el.dom.value = d.innerHTML;
50343 this.owner.fireEvent('push', this, v);
50345 if (this.autoClean) {
50346 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
50347 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
50349 if (this.enableBlocks) {
50350 Roo.htmleditor.Block.initAll(this.doc.body);
50353 this.updateLanguage();
50355 var lc = this.doc.body.lastChild;
50356 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
50357 // add an extra line at the end.
50358 this.doc.body.appendChild(this.doc.createElement('br'));
50366 deferFocus : function(){
50367 this.focus.defer(10, this);
50371 focus : function(){
50372 if(this.win && !this.sourceEditMode){
50379 assignDocWin: function()
50381 var iframe = this.iframe;
50384 this.doc = iframe.contentWindow.document;
50385 this.win = iframe.contentWindow;
50387 // if (!Roo.get(this.frameId)) {
50390 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50391 // this.win = Roo.get(this.frameId).dom.contentWindow;
50393 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
50397 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50398 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
50403 initEditor : function(){
50404 //console.log("INIT EDITOR");
50405 this.assignDocWin();
50409 this.doc.designMode="on";
50411 this.doc.write(this.getDocMarkup());
50414 var dbody = (this.doc.body || this.doc.documentElement);
50415 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
50416 // this copies styles from the containing element into thsi one..
50417 // not sure why we need all of this..
50418 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
50420 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
50421 //ss['background-attachment'] = 'fixed'; // w3c
50422 dbody.bgProperties = 'fixed'; // ie
50423 dbody.setAttribute("translate", "no");
50425 //Roo.DomHelper.applyStyles(dbody, ss);
50426 Roo.EventManager.on(this.doc, {
50428 'mouseup': this.onEditorEvent,
50429 'dblclick': this.onEditorEvent,
50430 'click': this.onEditorEvent,
50431 'keyup': this.onEditorEvent,
50436 Roo.EventManager.on(this.doc, {
50437 'paste': this.onPasteEvent,
50441 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
50444 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
50445 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
50447 this.initialized = true;
50450 // initialize special key events - enter
50451 new Roo.htmleditor.KeyEnter({core : this});
50455 this.owner.fireEvent('initialize', this);
50458 // this is to prevent a href clicks resulting in a redirect?
50460 onPasteEvent : function(e,v)
50462 // I think we better assume paste is going to be a dirty load of rubish from word..
50464 // even pasting into a 'email version' of this widget will have to clean up that mess.
50465 var cd = (e.browserEvent.clipboardData || window.clipboardData);
50467 // check what type of paste - if it's an image, then handle it differently.
50468 if (cd.files && cd.files.length > 0) {
50470 var urlAPI = (window.createObjectURL && window) ||
50471 (window.URL && URL.revokeObjectURL && URL) ||
50472 (window.webkitURL && webkitURL);
50474 var url = urlAPI.createObjectURL( cd.files[0]);
50475 this.insertAtCursor('<img src=" + url + ">');
50478 if (cd.types.indexOf('text/html') < 0 ) {
50482 var html = cd.getData('text/html'); // clipboard event
50483 if (cd.types.indexOf('text/rtf') > -1) {
50484 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
50485 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
50490 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
50491 .map(function(g) { return g.toDataURL(); })
50492 .filter(function(g) { return g != 'about:blank'; });
50495 html = this.cleanWordChars(html);
50497 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
50500 var sn = this.getParentElement();
50501 // check if d contains a table, and prevent nesting??
50502 //Roo.log(d.getElementsByTagName('table'));
50504 //Roo.log(sn.closest('table'));
50505 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
50506 e.preventDefault();
50507 this.insertAtCursor("You can not nest tables");
50508 //Roo.log("prevent?"); // fixme -
50514 if (images.length > 0) {
50515 // replace all v:imagedata - with img.
50516 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
50517 Roo.each(ar, function(node) {
50518 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
50519 node.parentNode.removeChild(node);
50523 Roo.each(d.getElementsByTagName('img'), function(img, i) {
50524 img.setAttribute('src', images[i]);
50527 if (this.autoClean) {
50528 new Roo.htmleditor.FilterWord({ node : d });
50530 new Roo.htmleditor.FilterStyleToTag({ node : d });
50531 new Roo.htmleditor.FilterAttributes({
50533 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
50534 attrib_clean : ['href', 'src' ]
50536 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
50537 // should be fonts..
50538 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
50539 new Roo.htmleditor.FilterParagraph({ node : d });
50540 new Roo.htmleditor.FilterSpan({ node : d });
50541 new Roo.htmleditor.FilterLongBr({ node : d });
50542 new Roo.htmleditor.FilterComment({ node : d });
50546 if (this.enableBlocks) {
50548 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
50549 if (img.closest('figure')) { // assume!! that it's aready
50552 var fig = new Roo.htmleditor.BlockFigure({
50553 image_src : img.src
50555 fig.updateElement(img); // replace it..
50561 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
50562 if (this.enableBlocks) {
50563 Roo.htmleditor.Block.initAll(this.doc.body);
50567 e.preventDefault();
50569 // default behaveiour should be our local cleanup paste? (optional?)
50570 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
50571 //this.owner.fireEvent('paste', e, v);
50574 onDestroy : function(){
50580 //for (var i =0; i < this.toolbars.length;i++) {
50581 // // fixme - ask toolbars for heights?
50582 // this.toolbars[i].onDestroy();
50585 //this.wrap.dom.innerHTML = '';
50586 //this.wrap.remove();
50591 onFirstFocus : function(){
50593 this.assignDocWin();
50594 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
50596 this.activated = true;
50599 if(Roo.isGecko){ // prevent silly gecko errors
50601 var s = this.win.getSelection();
50602 if(!s.focusNode || s.focusNode.nodeType != 3){
50603 var r = s.getRangeAt(0);
50604 r.selectNodeContents((this.doc.body || this.doc.documentElement));
50609 this.execCmd('useCSS', true);
50610 this.execCmd('styleWithCSS', false);
50613 this.owner.fireEvent('activate', this);
50617 adjustFont: function(btn){
50618 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
50619 //if(Roo.isSafari){ // safari
50622 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
50623 if(Roo.isSafari){ // safari
50624 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
50625 v = (v < 10) ? 10 : v;
50626 v = (v > 48) ? 48 : v;
50627 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
50632 v = Math.max(1, v+adjust);
50634 this.execCmd('FontSize', v );
50637 onEditorEvent : function(e)
50641 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
50642 return; // we do not handle this.. (undo manager does..)
50644 // in theory this detects if the last element is not a br, then we try and do that.
50645 // its so clicking in space at bottom triggers adding a br and moving the cursor.
50647 e.target.nodeName == 'BODY' &&
50648 e.type == "mouseup" &&
50649 this.doc.body.lastChild
50651 var lc = this.doc.body.lastChild;
50652 // gtx-trans is google translate plugin adding crap.
50653 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
50654 lc = lc.previousSibling;
50656 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
50657 // if last element is <BR> - then dont do anything.
50659 var ns = this.doc.createElement('br');
50660 this.doc.body.appendChild(ns);
50661 range = this.doc.createRange();
50662 range.setStartAfter(ns);
50663 range.collapse(true);
50664 var sel = this.win.getSelection();
50665 sel.removeAllRanges();
50666 sel.addRange(range);
50672 this.fireEditorEvent(e);
50673 // this.updateToolbar();
50674 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
50677 fireEditorEvent: function(e)
50679 this.owner.fireEvent('editorevent', this, e);
50682 insertTag : function(tg)
50684 // could be a bit smarter... -> wrap the current selected tRoo..
50685 if (tg.toLowerCase() == 'span' ||
50686 tg.toLowerCase() == 'code' ||
50687 tg.toLowerCase() == 'sup' ||
50688 tg.toLowerCase() == 'sub'
50691 range = this.createRange(this.getSelection());
50692 var wrappingNode = this.doc.createElement(tg.toLowerCase());
50693 wrappingNode.appendChild(range.extractContents());
50694 range.insertNode(wrappingNode);
50701 this.execCmd("formatblock", tg);
50702 this.undoManager.addEvent();
50705 insertText : function(txt)
50709 var range = this.createRange();
50710 range.deleteContents();
50711 //alert(Sender.getAttribute('label'));
50713 range.insertNode(this.doc.createTextNode(txt));
50714 this.undoManager.addEvent();
50720 * Executes a Midas editor command on the editor document and performs necessary focus and
50721 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
50722 * @param {String} cmd The Midas command
50723 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50725 relayCmd : function(cmd, value)
50729 case 'justifyleft':
50730 case 'justifyright':
50731 case 'justifycenter':
50732 // if we are in a cell, then we will adjust the
50733 var n = this.getParentElement();
50734 var td = n.closest('td');
50736 var bl = Roo.htmleditor.Block.factory(td);
50737 bl.textAlign = cmd.replace('justify','');
50738 bl.updateElement();
50739 this.owner.fireEvent('editorevent', this);
50742 this.execCmd('styleWithCSS', true); //
50746 // if there is no selection, then we insert, and set the curson inside it..
50747 this.execCmd('styleWithCSS', false);
50757 this.execCmd(cmd, value);
50758 this.owner.fireEvent('editorevent', this);
50759 //this.updateToolbar();
50760 this.owner.deferFocus();
50764 * Executes a Midas editor command directly on the editor document.
50765 * For visual commands, you should use {@link #relayCmd} instead.
50766 * <b>This should only be called after the editor is initialized.</b>
50767 * @param {String} cmd The Midas command
50768 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50770 execCmd : function(cmd, value){
50771 this.doc.execCommand(cmd, false, value === undefined ? null : value);
50778 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
50780 * @param {String} text | dom node..
50782 insertAtCursor : function(text)
50785 if(!this.activated){
50789 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
50793 // from jquery ui (MIT licenced)
50795 var win = this.win;
50797 if (win.getSelection && win.getSelection().getRangeAt) {
50799 // delete the existing?
50801 this.createRange(this.getSelection()).deleteContents();
50802 range = win.getSelection().getRangeAt(0);
50803 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
50804 range.insertNode(node);
50805 range = range.cloneRange();
50806 range.collapse(false);
50808 win.getSelection().removeAllRanges();
50809 win.getSelection().addRange(range);
50813 } else if (win.document.selection && win.document.selection.createRange) {
50814 // no firefox support
50815 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50816 win.document.selection.createRange().pasteHTML(txt);
50819 // no firefox support
50820 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50821 this.execCmd('InsertHTML', txt);
50829 mozKeyPress : function(e){
50831 var c = e.getCharCode(), cmd;
50834 c = String.fromCharCode(c).toLowerCase();
50848 // this.cleanUpPaste.defer(100, this);
50854 this.relayCmd(cmd);
50855 //this.win.focus();
50856 //this.execCmd(cmd);
50857 //this.deferFocus();
50858 e.preventDefault();
50866 fixKeys : function(){ // load time branching for fastest keydown performance
50870 return function(e){
50871 var k = e.getKey(), r;
50874 r = this.doc.selection.createRange();
50877 r.pasteHTML('    ');
50882 /// this is handled by Roo.htmleditor.KeyEnter
50885 r = this.doc.selection.createRange();
50887 var target = r.parentElement();
50888 if(!target || target.tagName.toLowerCase() != 'li'){
50890 r.pasteHTML('<br/>');
50897 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50898 // this.cleanUpPaste.defer(100, this);
50904 }else if(Roo.isOpera){
50905 return function(e){
50906 var k = e.getKey();
50910 this.execCmd('InsertHTML','    ');
50914 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50915 // this.cleanUpPaste.defer(100, this);
50920 }else if(Roo.isSafari){
50921 return function(e){
50922 var k = e.getKey();
50926 this.execCmd('InsertText','\t');
50930 this.mozKeyPress(e);
50932 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50933 // this.cleanUpPaste.defer(100, this);
50941 getAllAncestors: function()
50943 var p = this.getSelectedNode();
50946 a.push(p); // push blank onto stack..
50947 p = this.getParentElement();
50951 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
50955 a.push(this.doc.body);
50959 lastSelNode : false,
50962 getSelection : function()
50964 this.assignDocWin();
50965 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
50968 * Select a dom node
50969 * @param {DomElement} node the node to select
50971 selectNode : function(node, collapse)
50973 var nodeRange = node.ownerDocument.createRange();
50975 nodeRange.selectNode(node);
50977 nodeRange.selectNodeContents(node);
50979 if (collapse === true) {
50980 nodeRange.collapse(true);
50983 var s = this.win.getSelection();
50984 s.removeAllRanges();
50985 s.addRange(nodeRange);
50988 getSelectedNode: function()
50990 // this may only work on Gecko!!!
50992 // should we cache this!!!!
50996 var range = this.createRange(this.getSelection()).cloneRange();
50999 var parent = range.parentElement();
51001 var testRange = range.duplicate();
51002 testRange.moveToElementText(parent);
51003 if (testRange.inRange(range)) {
51006 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
51009 parent = parent.parentElement;
51014 // is ancestor a text element.
51015 var ac = range.commonAncestorContainer;
51016 if (ac.nodeType == 3) {
51017 ac = ac.parentNode;
51020 var ar = ac.childNodes;
51023 var other_nodes = [];
51024 var has_other_nodes = false;
51025 for (var i=0;i<ar.length;i++) {
51026 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
51029 // fullly contained node.
51031 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
51036 // probably selected..
51037 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
51038 other_nodes.push(ar[i]);
51042 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
51047 has_other_nodes = true;
51049 if (!nodes.length && other_nodes.length) {
51050 nodes= other_nodes;
51052 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
51060 createRange: function(sel)
51062 // this has strange effects when using with
51063 // top toolbar - not sure if it's a great idea.
51064 //this.editor.contentWindow.focus();
51065 if (typeof sel != "undefined") {
51067 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
51069 return this.doc.createRange();
51072 return this.doc.createRange();
51075 getParentElement: function()
51078 this.assignDocWin();
51079 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
51081 var range = this.createRange(sel);
51084 var p = range.commonAncestorContainer;
51085 while (p.nodeType == 3) { // text node
51096 * Range intersection.. the hard stuff...
51100 * [ -- selected range --- ]
51104 * if end is before start or hits it. fail.
51105 * if start is after end or hits it fail.
51107 * if either hits (but other is outside. - then it's not
51113 // @see http://www.thismuchiknow.co.uk/?p=64.
51114 rangeIntersectsNode : function(range, node)
51116 var nodeRange = node.ownerDocument.createRange();
51118 nodeRange.selectNode(node);
51120 nodeRange.selectNodeContents(node);
51123 var rangeStartRange = range.cloneRange();
51124 rangeStartRange.collapse(true);
51126 var rangeEndRange = range.cloneRange();
51127 rangeEndRange.collapse(false);
51129 var nodeStartRange = nodeRange.cloneRange();
51130 nodeStartRange.collapse(true);
51132 var nodeEndRange = nodeRange.cloneRange();
51133 nodeEndRange.collapse(false);
51135 return rangeStartRange.compareBoundaryPoints(
51136 Range.START_TO_START, nodeEndRange) == -1 &&
51137 rangeEndRange.compareBoundaryPoints(
51138 Range.START_TO_START, nodeStartRange) == 1;
51142 rangeCompareNode : function(range, node)
51144 var nodeRange = node.ownerDocument.createRange();
51146 nodeRange.selectNode(node);
51148 nodeRange.selectNodeContents(node);
51152 range.collapse(true);
51154 nodeRange.collapse(true);
51156 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
51157 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
51159 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
51161 var nodeIsBefore = ss == 1;
51162 var nodeIsAfter = ee == -1;
51164 if (nodeIsBefore && nodeIsAfter) {
51167 if (!nodeIsBefore && nodeIsAfter) {
51168 return 1; //right trailed.
51171 if (nodeIsBefore && !nodeIsAfter) {
51172 return 2; // left trailed.
51178 cleanWordChars : function(input) {// change the chars to hex code
51181 [ 8211, "–" ],
51182 [ 8212, "—" ],
51190 var output = input;
51191 Roo.each(swapCodes, function(sw) {
51192 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
51194 output = output.replace(swapper, sw[1]);
51204 cleanUpChild : function (node)
51207 new Roo.htmleditor.FilterComment({node : node});
51208 new Roo.htmleditor.FilterAttributes({
51210 attrib_black : this.ablack,
51211 attrib_clean : this.aclean,
51212 style_white : this.cwhite,
51213 style_black : this.cblack
51215 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
51216 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
51222 * Clean up MS wordisms...
51223 * @deprecated - use filter directly
51225 cleanWord : function(node)
51227 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
51228 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
51235 * @deprecated - use filters
51237 cleanTableWidths : function(node)
51239 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
51246 applyBlacklists : function()
51248 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
51249 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
51251 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
51252 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
51253 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
51257 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
51258 if (b.indexOf(tag) > -1) {
51261 this.white.push(tag);
51265 Roo.each(w, function(tag) {
51266 if (b.indexOf(tag) > -1) {
51269 if (this.white.indexOf(tag) > -1) {
51272 this.white.push(tag);
51277 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
51278 if (w.indexOf(tag) > -1) {
51281 this.black.push(tag);
51285 Roo.each(b, function(tag) {
51286 if (w.indexOf(tag) > -1) {
51289 if (this.black.indexOf(tag) > -1) {
51292 this.black.push(tag);
51297 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
51298 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
51302 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
51303 if (b.indexOf(tag) > -1) {
51306 this.cwhite.push(tag);
51310 Roo.each(w, function(tag) {
51311 if (b.indexOf(tag) > -1) {
51314 if (this.cwhite.indexOf(tag) > -1) {
51317 this.cwhite.push(tag);
51322 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
51323 if (w.indexOf(tag) > -1) {
51326 this.cblack.push(tag);
51330 Roo.each(b, function(tag) {
51331 if (w.indexOf(tag) > -1) {
51334 if (this.cblack.indexOf(tag) > -1) {
51337 this.cblack.push(tag);
51342 setStylesheets : function(stylesheets)
51344 if(typeof(stylesheets) == 'string'){
51345 Roo.get(this.iframe.contentDocument.head).createChild({
51347 rel : 'stylesheet',
51356 Roo.each(stylesheets, function(s) {
51361 Roo.get(_this.iframe.contentDocument.head).createChild({
51363 rel : 'stylesheet',
51373 updateLanguage : function()
51375 if (!this.iframe || !this.iframe.contentDocument) {
51378 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
51382 removeStylesheets : function()
51386 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
51391 setStyle : function(style)
51393 Roo.get(this.iframe.contentDocument.head).createChild({
51402 // hide stuff that is not compatible
51416 * @event specialkey
51420 * @cfg {String} fieldClass @hide
51423 * @cfg {String} focusClass @hide
51426 * @cfg {String} autoCreate @hide
51429 * @cfg {String} inputType @hide
51432 * @cfg {String} invalidClass @hide
51435 * @cfg {String} invalidText @hide
51438 * @cfg {String} msgFx @hide
51441 * @cfg {String} validateOnBlur @hide
51445 Roo.HtmlEditorCore.white = [
51446 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
51448 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
51449 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
51450 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
51451 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
51452 'TABLE', 'UL', 'XMP',
51454 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
51457 'DIR', 'MENU', 'OL', 'UL', 'DL',
51463 Roo.HtmlEditorCore.black = [
51464 // 'embed', 'object', // enable - backend responsiblity to clean thiese
51466 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
51467 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
51468 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
51469 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
51470 //'FONT' // CLEAN LATER..
51471 'COLGROUP', 'COL' // messy tables.
51475 Roo.HtmlEditorCore.clean = [ // ?? needed???
51476 'SCRIPT', 'STYLE', 'TITLE', 'XML'
51478 Roo.HtmlEditorCore.tag_remove = [
51483 Roo.HtmlEditorCore.ablack = [
51487 Roo.HtmlEditorCore.aclean = [
51488 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
51492 Roo.HtmlEditorCore.pwhite= [
51493 'http', 'https', 'mailto'
51496 // white listed style attributes.
51497 Roo.HtmlEditorCore.cwhite= [
51498 // 'text-align', /// default is to allow most things..
51504 // black listed style attributes.
51505 Roo.HtmlEditorCore.cblack= [
51506 // 'font-size' -- this can be set by the project
51512 //<script type="text/javascript">
51515 * Ext JS Library 1.1.1
51516 * Copyright(c) 2006-2007, Ext JS, LLC.
51522 Roo.form.HtmlEditor = function(config){
51526 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
51528 if (!this.toolbars) {
51529 this.toolbars = [];
51531 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
51537 * @class Roo.form.HtmlEditor
51538 * @extends Roo.form.Field
51539 * Provides a lightweight HTML Editor component.
51541 * This has been tested on Fireforx / Chrome.. IE may not be so great..
51543 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
51544 * supported by this editor.</b><br/><br/>
51545 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
51546 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
51548 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
51550 * @cfg {Boolean} clearUp
51554 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
51559 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
51564 * @cfg {Number} height (in pixels)
51568 * @cfg {Number} width (in pixels)
51573 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea rootURL + '/roojs1/css/undoreset.css', .
51576 stylesheets: false,
51580 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
51585 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
51591 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
51596 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
51601 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
51603 allowComments: false,
51605 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
51607 enableBlocks : true,
51610 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
51611 * if you are doing an email editor, this probably needs disabling, it's designed
51615 * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
51619 * @cfg {String} language default en - language of text (usefull for rtl languages)
51628 // private properties
51629 validationEvent : false,
51631 initialized : false,
51634 onFocus : Roo.emptyFn,
51636 hideMode:'offsets',
51638 actionMode : 'container', // defaults to hiding it...
51640 defaultAutoCreate : { // modified by initCompnoent..
51642 style:"width:500px;height:300px;",
51643 autocomplete: "new-password"
51647 initComponent : function(){
51650 * @event initialize
51651 * Fires when the editor is fully initialized (including the iframe)
51652 * @param {HtmlEditor} this
51657 * Fires when the editor is first receives the focus. Any insertion must wait
51658 * until after this event.
51659 * @param {HtmlEditor} this
51663 * @event beforesync
51664 * Fires before the textarea is updated with content from the editor iframe. Return false
51665 * to cancel the sync.
51666 * @param {HtmlEditor} this
51667 * @param {String} html
51671 * @event beforepush
51672 * Fires before the iframe editor is updated with content from the textarea. Return false
51673 * to cancel the push.
51674 * @param {HtmlEditor} this
51675 * @param {String} html
51680 * Fires when the textarea is updated with content from the editor iframe.
51681 * @param {HtmlEditor} this
51682 * @param {String} html
51687 * Fires when the iframe editor is updated with content from the textarea.
51688 * @param {HtmlEditor} this
51689 * @param {String} html
51693 * @event editmodechange
51694 * Fires when the editor switches edit modes
51695 * @param {HtmlEditor} this
51696 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
51698 editmodechange: true,
51700 * @event editorevent
51701 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
51702 * @param {HtmlEditor} this
51706 * @event firstfocus
51707 * Fires when on first focus - needed by toolbars..
51708 * @param {HtmlEditor} this
51713 * Auto save the htmlEditor value as a file into Events
51714 * @param {HtmlEditor} this
51718 * @event savedpreview
51719 * preview the saved version of htmlEditor
51720 * @param {HtmlEditor} this
51722 savedpreview: true,
51725 * @event stylesheetsclick
51726 * Fires when press the Sytlesheets button
51727 * @param {Roo.HtmlEditorCore} this
51729 stylesheetsclick: true,
51732 * Fires when press user pastes into the editor
51733 * @param {Roo.HtmlEditorCore} this
51737 this.defaultAutoCreate = {
51739 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
51740 autocomplete: "new-password"
51745 * Protected method that will not generally be called directly. It
51746 * is called when the editor creates its toolbar. Override this method if you need to
51747 * add custom toolbar buttons.
51748 * @param {HtmlEditor} editor
51750 createToolbar : function(editor){
51751 Roo.log("create toolbars");
51752 if (!editor.toolbars || !editor.toolbars.length) {
51753 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
51756 for (var i =0 ; i < editor.toolbars.length;i++) {
51757 editor.toolbars[i] = Roo.factory(
51758 typeof(editor.toolbars[i]) == 'string' ?
51759 { xtype: editor.toolbars[i]} : editor.toolbars[i],
51760 Roo.form.HtmlEditor);
51761 editor.toolbars[i].init(editor);
51767 * get the Context selected node
51768 * @returns {DomElement|boolean} selected node if active or false if none
51771 getSelectedNode : function()
51773 if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
51776 return this.toolbars[1].tb.selectedNode;
51780 onRender : function(ct, position)
51783 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
51785 this.wrap = this.el.wrap({
51786 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
51789 this.editorcore.onRender(ct, position);
51791 if (this.resizable) {
51792 this.resizeEl = new Roo.Resizable(this.wrap, {
51796 minHeight : this.height,
51797 height: this.height,
51798 handles : this.resizable,
51801 resize : function(r, w, h) {
51802 _t.onResize(w,h); // -something
51808 this.createToolbar(this);
51812 this.setSize(this.wrap.getSize());
51814 if (this.resizeEl) {
51815 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
51816 // should trigger onReize..
51819 this.keyNav = new Roo.KeyNav(this.el, {
51821 "tab" : function(e){
51822 e.preventDefault();
51824 var value = this.getValue();
51826 var start = this.el.dom.selectionStart;
51827 var end = this.el.dom.selectionEnd;
51831 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
51832 this.el.dom.setSelectionRange(end + 1, end + 1);
51836 var f = value.substring(0, start).split("\t");
51838 if(f.pop().length != 0){
51842 this.setValue(f.join("\t") + value.substring(end));
51843 this.el.dom.setSelectionRange(start - 1, start - 1);
51847 "home" : function(e){
51848 e.preventDefault();
51850 var curr = this.el.dom.selectionStart;
51851 var lines = this.getValue().split("\n");
51858 this.el.dom.setSelectionRange(0, 0);
51864 for (var i = 0; i < lines.length;i++) {
51865 pos += lines[i].length;
51875 pos -= lines[i].length;
51881 this.el.dom.setSelectionRange(pos, pos);
51885 this.el.dom.selectionStart = pos;
51886 this.el.dom.selectionEnd = curr;
51889 "end" : function(e){
51890 e.preventDefault();
51892 var curr = this.el.dom.selectionStart;
51893 var lines = this.getValue().split("\n");
51900 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
51906 for (var i = 0; i < lines.length;i++) {
51908 pos += lines[i].length;
51922 this.el.dom.setSelectionRange(pos, pos);
51926 this.el.dom.selectionStart = curr;
51927 this.el.dom.selectionEnd = pos;
51932 doRelay : function(foo, bar, hname){
51933 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
51939 // if(this.autosave && this.w){
51940 // this.autoSaveFn = setInterval(this.autosave, 1000);
51945 onResize : function(w, h)
51947 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
51952 if(typeof w == 'number'){
51953 var aw = w - this.wrap.getFrameWidth('lr');
51954 this.el.setWidth(this.adjustWidth('textarea', aw));
51957 if(typeof h == 'number'){
51959 for (var i =0; i < this.toolbars.length;i++) {
51960 // fixme - ask toolbars for heights?
51961 tbh += this.toolbars[i].tb.el.getHeight();
51962 if (this.toolbars[i].footer) {
51963 tbh += this.toolbars[i].footer.el.getHeight();
51970 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
51971 ah -= 5; // knock a few pixes off for look..
51973 this.el.setHeight(this.adjustWidth('textarea', ah));
51977 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
51978 this.editorcore.onResize(ew,eh);
51983 * Toggles the editor between standard and source edit mode.
51984 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
51986 toggleSourceEdit : function(sourceEditMode)
51988 this.editorcore.toggleSourceEdit(sourceEditMode);
51990 if(this.editorcore.sourceEditMode){
51991 Roo.log('editor - showing textarea');
51994 // Roo.log(this.syncValue());
51995 this.editorcore.syncValue();
51996 this.el.removeClass('x-hidden');
51997 this.el.dom.removeAttribute('tabIndex');
51999 this.el.dom.scrollTop = 0;
52002 for (var i = 0; i < this.toolbars.length; i++) {
52003 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
52004 this.toolbars[i].tb.hide();
52005 this.toolbars[i].footer.hide();
52010 Roo.log('editor - hiding textarea');
52012 // Roo.log(this.pushValue());
52013 this.editorcore.pushValue();
52015 this.el.addClass('x-hidden');
52016 this.el.dom.setAttribute('tabIndex', -1);
52018 for (var i = 0; i < this.toolbars.length; i++) {
52019 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
52020 this.toolbars[i].tb.show();
52021 this.toolbars[i].footer.show();
52025 //this.deferFocus();
52028 this.setSize(this.wrap.getSize());
52029 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
52031 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
52034 // private (for BoxComponent)
52035 adjustSize : Roo.BoxComponent.prototype.adjustSize,
52037 // private (for BoxComponent)
52038 getResizeEl : function(){
52042 // private (for BoxComponent)
52043 getPositionEl : function(){
52048 initEvents : function(){
52049 this.originalValue = this.getValue();
52053 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
52056 markInvalid : Roo.emptyFn,
52058 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
52061 clearInvalid : Roo.emptyFn,
52063 setValue : function(v){
52064 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
52065 this.editorcore.pushValue();
52069 * update the language in the body - really done by core
52070 * @param {String} language - eg. en / ar / zh-CN etc..
52072 updateLanguage : function(lang)
52074 this.language = lang;
52075 this.editorcore.language = lang;
52076 this.editorcore.updateLanguage();
52080 deferFocus : function(){
52081 this.focus.defer(10, this);
52085 focus : function(){
52086 this.editorcore.focus();
52092 onDestroy : function(){
52098 for (var i =0; i < this.toolbars.length;i++) {
52099 // fixme - ask toolbars for heights?
52100 this.toolbars[i].onDestroy();
52103 this.wrap.dom.innerHTML = '';
52104 this.wrap.remove();
52109 onFirstFocus : function(){
52110 //Roo.log("onFirstFocus");
52111 this.editorcore.onFirstFocus();
52112 for (var i =0; i < this.toolbars.length;i++) {
52113 this.toolbars[i].onFirstFocus();
52119 syncValue : function()
52121 this.editorcore.syncValue();
52124 pushValue : function()
52126 this.editorcore.pushValue();
52129 setStylesheets : function(stylesheets)
52131 this.editorcore.setStylesheets(stylesheets);
52134 removeStylesheets : function()
52136 this.editorcore.removeStylesheets();
52140 // hide stuff that is not compatible
52154 * @event specialkey
52158 * @cfg {String} fieldClass @hide
52161 * @cfg {String} focusClass @hide
52164 * @cfg {String} autoCreate @hide
52167 * @cfg {String} inputType @hide
52170 * @cfg {String} invalidClass @hide
52173 * @cfg {String} invalidText @hide
52176 * @cfg {String} msgFx @hide
52179 * @cfg {String} validateOnBlur @hide
52185 * Ext JS Library 1.1.1
52186 * Copyright(c) 2006-2007, Ext JS, LLC.
52192 * @class Roo.form.HtmlEditor.ToolbarStandard
52197 new Roo.form.HtmlEditor({
52200 new Roo.form.HtmlEditorToolbar1({
52201 disable : { fonts: 1 , format: 1, ..., ... , ...],
52207 * @cfg {Object} disable List of elements to disable..
52208 * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
52212 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
52215 Roo.form.HtmlEditor.ToolbarStandard = function(config)
52218 Roo.apply(this, config);
52220 // default disabled, based on 'good practice'..
52221 this.disable = this.disable || {};
52222 Roo.applyIf(this.disable, {
52225 specialElements : true
52229 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
52230 // dont call parent... till later.
52233 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
52240 editorcore : false,
52242 * @cfg {Object} disable List of toolbar elements to disable
52249 * @cfg {String} createLinkText The default text for the create link prompt
52251 createLinkText : 'Please enter the URL for the link:',
52253 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
52255 defaultLinkValue : 'http:/'+'/',
52259 * @cfg {Array} fontFamilies An array of available font families
52277 // "á" , ?? a acute?
52282 "°" // , // degrees
52284 // "é" , // e ecute
52285 // "ú" , // u ecute?
52288 specialElements : [
52290 text: "Insert Table",
52293 ihtml : '<table><tr><td>Cell</td></tr></table>'
52297 text: "Insert Image",
52300 ihtml : '<img src="about:blank"/>'
52309 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
52310 "input:submit", "input:button", "select", "textarea", "label" ],
52313 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
52315 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
52324 * @cfg {String} defaultFont default font to use.
52326 defaultFont: 'tahoma',
52328 fontSelect : false,
52331 formatCombo : false,
52333 init : function(editor)
52335 this.editor = editor;
52336 this.editorcore = editor.editorcore ? editor.editorcore : editor;
52337 var editorcore = this.editorcore;
52341 var fid = editorcore.frameId;
52343 function btn(id, toggle, handler){
52344 var xid = fid + '-'+ id ;
52348 cls : 'x-btn-icon x-edit-'+id,
52349 enableToggle:toggle !== false,
52350 scope: _t, // was editor...
52351 handler:handler||_t.relayBtnCmd,
52352 clickEvent:'mousedown',
52353 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52360 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52362 // stop form submits
52363 tb.el.on('click', function(e){
52364 e.preventDefault(); // what does this do?
52367 if(!this.disable.font) { // && !Roo.isSafari){
52368 /* why no safari for fonts
52369 editor.fontSelect = tb.el.createChild({
52372 cls:'x-font-select',
52373 html: this.createFontOptions()
52376 editor.fontSelect.on('change', function(){
52377 var font = editor.fontSelect.dom.value;
52378 editor.relayCmd('fontname', font);
52379 editor.deferFocus();
52383 editor.fontSelect.dom,
52389 if(!this.disable.formats){
52390 this.formatCombo = new Roo.form.ComboBox({
52391 store: new Roo.data.SimpleStore({
52394 data : this.formats // from states.js
52398 //autoCreate : {tag: "div", size: "20"},
52399 displayField:'tag',
52403 triggerAction: 'all',
52404 emptyText:'Add tag',
52405 selectOnFocus:true,
52408 'select': function(c, r, i) {
52409 editorcore.insertTag(r.get('tag'));
52415 tb.addField(this.formatCombo);
52419 if(!this.disable.format){
52424 btn('strikethrough')
52427 if(!this.disable.fontSize){
52432 btn('increasefontsize', false, editorcore.adjustFont),
52433 btn('decreasefontsize', false, editorcore.adjustFont)
52438 if(!this.disable.colors){
52441 id:editorcore.frameId +'-forecolor',
52442 cls:'x-btn-icon x-edit-forecolor',
52443 clickEvent:'mousedown',
52444 tooltip: this.buttonTips['forecolor'] || undefined,
52446 menu : new Roo.menu.ColorMenu({
52447 allowReselect: true,
52448 focus: Roo.emptyFn,
52451 selectHandler: function(cp, color){
52452 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
52453 editor.deferFocus();
52456 clickEvent:'mousedown'
52459 id:editorcore.frameId +'backcolor',
52460 cls:'x-btn-icon x-edit-backcolor',
52461 clickEvent:'mousedown',
52462 tooltip: this.buttonTips['backcolor'] || undefined,
52464 menu : new Roo.menu.ColorMenu({
52465 focus: Roo.emptyFn,
52468 allowReselect: true,
52469 selectHandler: function(cp, color){
52471 editorcore.execCmd('useCSS', false);
52472 editorcore.execCmd('hilitecolor', color);
52473 editorcore.execCmd('useCSS', true);
52474 editor.deferFocus();
52476 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
52477 Roo.isSafari || Roo.isIE ? '#'+color : color);
52478 editor.deferFocus();
52482 clickEvent:'mousedown'
52487 // now add all the items...
52490 if(!this.disable.alignments){
52493 btn('justifyleft'),
52494 btn('justifycenter'),
52495 btn('justifyright')
52499 //if(!Roo.isSafari){
52500 if(!this.disable.links){
52503 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
52507 if(!this.disable.lists){
52510 btn('insertorderedlist'),
52511 btn('insertunorderedlist')
52514 if(!this.disable.sourceEdit){
52517 btn('sourceedit', true, function(btn){
52518 this.toggleSourceEdit(btn.pressed);
52525 // special menu.. - needs to be tidied up..
52526 if (!this.disable.special) {
52529 cls: 'x-edit-none',
52535 for (var i =0; i < this.specialChars.length; i++) {
52536 smenu.menu.items.push({
52538 html: this.specialChars[i],
52539 handler: function(a,b) {
52540 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
52541 //editor.insertAtCursor(a.html);
52555 if (!this.disable.cleanStyles) {
52557 cls: 'x-btn-icon x-btn-clear',
52563 for (var i =0; i < this.cleanStyles.length; i++) {
52564 cmenu.menu.items.push({
52565 actiontype : this.cleanStyles[i],
52566 html: 'Remove ' + this.cleanStyles[i],
52567 handler: function(a,b) {
52570 var c = Roo.get(editorcore.doc.body);
52571 c.select('[style]').each(function(s) {
52572 s.dom.style.removeProperty(a.actiontype);
52574 editorcore.syncValue();
52579 cmenu.menu.items.push({
52580 actiontype : 'tablewidths',
52581 html: 'Remove Table Widths',
52582 handler: function(a,b) {
52583 editorcore.cleanTableWidths();
52584 editorcore.syncValue();
52588 cmenu.menu.items.push({
52589 actiontype : 'word',
52590 html: 'Remove MS Word Formating',
52591 handler: function(a,b) {
52592 editorcore.cleanWord();
52593 editorcore.syncValue();
52598 cmenu.menu.items.push({
52599 actiontype : 'all',
52600 html: 'Remove All Styles',
52601 handler: function(a,b) {
52603 var c = Roo.get(editorcore.doc.body);
52604 c.select('[style]').each(function(s) {
52605 s.dom.removeAttribute('style');
52607 editorcore.syncValue();
52612 cmenu.menu.items.push({
52613 actiontype : 'all',
52614 html: 'Remove All CSS Classes',
52615 handler: function(a,b) {
52617 var c = Roo.get(editorcore.doc.body);
52618 c.select('[class]').each(function(s) {
52619 s.dom.removeAttribute('class');
52621 editorcore.cleanWord();
52622 editorcore.syncValue();
52627 cmenu.menu.items.push({
52628 actiontype : 'tidy',
52629 html: 'Tidy HTML Source',
52630 handler: function(a,b) {
52631 new Roo.htmleditor.Tidy(editorcore.doc.body);
52632 editorcore.syncValue();
52641 if (!this.disable.specialElements) {
52644 cls: 'x-edit-none',
52649 for (var i =0; i < this.specialElements.length; i++) {
52650 semenu.menu.items.push(
52652 handler: function(a,b) {
52653 editor.insertAtCursor(this.ihtml);
52655 }, this.specialElements[i])
52667 for(var i =0; i< this.btns.length;i++) {
52668 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
52669 b.cls = 'x-edit-none';
52671 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
52672 b.cls += ' x-init-enable';
52675 b.scope = editorcore;
52683 // disable everything...
52685 this.tb.items.each(function(item){
52688 item.id != editorcore.frameId+ '-sourceedit' &&
52689 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
52695 this.rendered = true;
52697 // the all the btns;
52698 editor.on('editorevent', this.updateToolbar, this);
52699 // other toolbars need to implement this..
52700 //editor.on('editmodechange', this.updateToolbar, this);
52704 relayBtnCmd : function(btn) {
52705 this.editorcore.relayCmd(btn.cmd);
52707 // private used internally
52708 createLink : function(){
52709 //Roo.log("create link?");
52710 var ec = this.editorcore;
52711 var ar = ec.getAllAncestors();
52713 for(var i = 0;i< ar.length;i++) {
52714 if (ar[i] && ar[i].nodeName == 'A') {
52722 Roo.MessageBox.show({
52723 title : "Add / Edit Link URL",
52724 msg : "Enter the url for the link",
52725 buttons: Roo.MessageBox.OKCANCEL,
52726 fn: function(btn, url){
52730 if(url && url != 'http:/'+'/'){
52732 n.setAttribute('href', url);
52734 ec.relayCmd('createlink', url);
52740 //multiline: multiline,
52742 value : n ? n.getAttribute('href') : ''
52746 }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
52752 * Protected method that will not generally be called directly. It triggers
52753 * a toolbar update by reading the markup state of the current selection in the editor.
52755 updateToolbar: function(){
52757 if(!this.editorcore.activated){
52758 this.editor.onFirstFocus();
52762 var btns = this.tb.items.map,
52763 doc = this.editorcore.doc,
52764 frameId = this.editorcore.frameId;
52766 if(!this.disable.font && !Roo.isSafari){
52768 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
52769 if(name != this.fontSelect.dom.value){
52770 this.fontSelect.dom.value = name;
52774 if(!this.disable.format){
52775 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
52776 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
52777 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
52778 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
52780 if(!this.disable.alignments){
52781 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
52782 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
52783 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
52785 if(!Roo.isSafari && !this.disable.lists){
52786 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
52787 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
52790 var ans = this.editorcore.getAllAncestors();
52791 if (this.formatCombo) {
52794 var store = this.formatCombo.store;
52795 this.formatCombo.setValue("");
52796 for (var i =0; i < ans.length;i++) {
52797 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
52799 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
52807 // hides menus... - so this cant be on a menu...
52808 Roo.menu.MenuMgr.hideAll();
52810 //this.editorsyncValue();
52814 createFontOptions : function(){
52815 var buf = [], fs = this.fontFamilies, ff, lc;
52819 for(var i = 0, len = fs.length; i< len; i++){
52821 lc = ff.toLowerCase();
52823 '<option value="',lc,'" style="font-family:',ff,';"',
52824 (this.defaultFont == lc ? ' selected="true">' : '>'),
52829 return buf.join('');
52832 toggleSourceEdit : function(sourceEditMode){
52834 Roo.log("toolbar toogle");
52835 if(sourceEditMode === undefined){
52836 sourceEditMode = !this.sourceEditMode;
52838 this.sourceEditMode = sourceEditMode === true;
52839 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
52840 // just toggle the button?
52841 if(btn.pressed !== this.sourceEditMode){
52842 btn.toggle(this.sourceEditMode);
52846 if(sourceEditMode){
52847 Roo.log("disabling buttons");
52848 this.tb.items.each(function(item){
52849 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
52855 Roo.log("enabling buttons");
52856 if(this.editorcore.initialized){
52857 this.tb.items.each(function(item){
52860 // initialize 'blocks'
52861 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
52862 Roo.htmleditor.Block.factory(e).updateElement(e);
52868 Roo.log("calling toggole on editor");
52869 // tell the editor that it's been pressed..
52870 this.editor.toggleSourceEdit(sourceEditMode);
52874 * Object collection of toolbar tooltips for the buttons in the editor. The key
52875 * is the command id associated with that button and the value is a valid QuickTips object.
52880 title: 'Bold (Ctrl+B)',
52881 text: 'Make the selected text bold.',
52882 cls: 'x-html-editor-tip'
52885 title: 'Italic (Ctrl+I)',
52886 text: 'Make the selected text italic.',
52887 cls: 'x-html-editor-tip'
52895 title: 'Bold (Ctrl+B)',
52896 text: 'Make the selected text bold.',
52897 cls: 'x-html-editor-tip'
52900 title: 'Italic (Ctrl+I)',
52901 text: 'Make the selected text italic.',
52902 cls: 'x-html-editor-tip'
52905 title: 'Underline (Ctrl+U)',
52906 text: 'Underline the selected text.',
52907 cls: 'x-html-editor-tip'
52910 title: 'Strikethrough',
52911 text: 'Strikethrough the selected text.',
52912 cls: 'x-html-editor-tip'
52914 increasefontsize : {
52915 title: 'Grow Text',
52916 text: 'Increase the font size.',
52917 cls: 'x-html-editor-tip'
52919 decreasefontsize : {
52920 title: 'Shrink Text',
52921 text: 'Decrease the font size.',
52922 cls: 'x-html-editor-tip'
52925 title: 'Text Highlight Color',
52926 text: 'Change the background color of the selected text.',
52927 cls: 'x-html-editor-tip'
52930 title: 'Font Color',
52931 text: 'Change the color of the selected text.',
52932 cls: 'x-html-editor-tip'
52935 title: 'Align Text Left',
52936 text: 'Align text to the left.',
52937 cls: 'x-html-editor-tip'
52940 title: 'Center Text',
52941 text: 'Center text in the editor.',
52942 cls: 'x-html-editor-tip'
52945 title: 'Align Text Right',
52946 text: 'Align text to the right.',
52947 cls: 'x-html-editor-tip'
52949 insertunorderedlist : {
52950 title: 'Bullet List',
52951 text: 'Start a bulleted list.',
52952 cls: 'x-html-editor-tip'
52954 insertorderedlist : {
52955 title: 'Numbered List',
52956 text: 'Start a numbered list.',
52957 cls: 'x-html-editor-tip'
52960 title: 'Hyperlink',
52961 text: 'Make the selected text a hyperlink.',
52962 cls: 'x-html-editor-tip'
52965 title: 'Source Edit',
52966 text: 'Switch to source editing mode.',
52967 cls: 'x-html-editor-tip'
52971 onDestroy : function(){
52974 this.tb.items.each(function(item){
52976 item.menu.removeAll();
52978 item.menu.el.destroy();
52986 onFirstFocus: function() {
52987 this.tb.items.each(function(item){
52996 // <script type="text/javascript">
52999 * Ext JS Library 1.1.1
53000 * Copyright(c) 2006-2007, Ext JS, LLC.
53007 * @class Roo.form.HtmlEditor.ToolbarContext
53012 new Roo.form.HtmlEditor({
53015 { xtype: 'ToolbarStandard', styles : {} }
53016 { xtype: 'ToolbarContext', disable : {} }
53022 * @config : {Object} disable List of elements to disable.. (not done yet.)
53023 * @config : {Object} styles Map of styles available.
53027 Roo.form.HtmlEditor.ToolbarContext = function(config)
53030 Roo.apply(this, config);
53031 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
53032 // dont call parent... till later.
53033 this.styles = this.styles || {};
53038 Roo.form.HtmlEditor.ToolbarContext.types = {
53053 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
53079 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
53150 name : 'selectoptions',
53156 // should we really allow this??
53157 // should this just be
53174 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
53175 Roo.form.HtmlEditor.ToolbarContext.stores = false;
53177 Roo.form.HtmlEditor.ToolbarContext.options = {
53179 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
53180 [ 'Courier New', 'Courier New'],
53181 [ 'Tahoma', 'Tahoma'],
53182 [ 'Times New Roman,serif', 'Times'],
53183 [ 'Verdana','Verdana' ]
53187 // fixme - these need to be configurable..
53190 //Roo.form.HtmlEditor.ToolbarContext.types
53193 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
53200 editorcore : false,
53202 * @cfg {Object} disable List of toolbar elements to disable
53207 * @cfg {Object} styles List of styles
53208 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
53210 * These must be defined in the page, so they get rendered correctly..
53221 init : function(editor)
53223 this.editor = editor;
53224 this.editorcore = editor.editorcore ? editor.editorcore : editor;
53225 var editorcore = this.editorcore;
53227 var fid = editorcore.frameId;
53229 function btn(id, toggle, handler){
53230 var xid = fid + '-'+ id ;
53234 cls : 'x-btn-icon x-edit-'+id,
53235 enableToggle:toggle !== false,
53236 scope: editorcore, // was editor...
53237 handler:handler||editorcore.relayBtnCmd,
53238 clickEvent:'mousedown',
53239 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53243 // create a new element.
53244 var wdiv = editor.wrap.createChild({
53246 }, editor.wrap.dom.firstChild.nextSibling, true);
53248 // can we do this more than once??
53250 // stop form submits
53253 // disable everything...
53254 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
53255 this.toolbars = {};
53256 // block toolbars are built in updateToolbar when needed.
53257 for (var i in ty) {
53259 this.toolbars[i] = this.buildToolbar(ty[i],i);
53261 this.tb = this.toolbars.BODY;
53263 this.buildFooter();
53264 this.footer.show();
53265 editor.on('hide', function( ) { this.footer.hide() }, this);
53266 editor.on('show', function( ) { this.footer.show() }, this);
53269 this.rendered = true;
53271 // the all the btns;
53272 editor.on('editorevent', this.updateToolbar, this);
53273 // other toolbars need to implement this..
53274 //editor.on('editmodechange', this.updateToolbar, this);
53280 * Protected method that will not generally be called directly. It triggers
53281 * a toolbar update by reading the markup state of the current selection in the editor.
53283 * Note you can force an update by calling on('editorevent', scope, false)
53285 updateToolbar: function(editor ,ev, sel)
53289 ev.stopEvent(); // se if we can stop this looping with mutiple events.
53293 // capture mouse up - this is handy for selecting images..
53294 // perhaps should go somewhere else...
53295 if(!this.editorcore.activated){
53296 this.editor.onFirstFocus();
53299 //Roo.log(ev ? ev.target : 'NOTARGET');
53302 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
53303 // selectNode - might want to handle IE?
53308 (ev.type == 'mouseup' || ev.type == 'click' ) &&
53309 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
53310 // they have click on an image...
53311 // let's see if we can change the selection...
53314 // this triggers looping?
53315 //this.editorcore.selectNode(sel);
53319 // this forces an id..
53320 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
53321 e.classList.remove('roo-ed-selection');
53323 //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
53324 //Roo.get(node).addClass('roo-ed-selection');
53326 //var updateFooter = sel ? false : true;
53329 var ans = this.editorcore.getAllAncestors();
53332 var ty = Roo.form.HtmlEditor.ToolbarContext.types;
53335 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
53336 sel = sel ? sel : this.editorcore.doc.body;
53337 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
53341 var tn = sel.tagName.toUpperCase();
53342 var lastSel = this.tb.selectedNode;
53343 this.tb.selectedNode = sel;
53344 var left_label = tn;
53346 // ok see if we are editing a block?
53349 // you are not actually selecting the block.
53350 if (sel && sel.hasAttribute('data-block')) {
53352 } else if (sel && sel.closest('[data-block]')) {
53354 db = sel.closest('[data-block]');
53355 //var cepar = sel.closest('[contenteditable=true]');
53356 //if (db && cepar && cepar.tagName != 'BODY') {
53357 // db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
53363 //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
53364 if (db && this.editorcore.enableBlocks) {
53365 block = Roo.htmleditor.Block.factory(db);
53370 db.classList.length > 0 ? db.className + ' ' : ''
53371 ) + 'roo-ed-selection';
53373 // since we removed it earlier... its not there..
53374 tn = 'BLOCK.' + db.getAttribute('data-block');
53376 //this.editorcore.selectNode(db);
53377 if (typeof(this.toolbars[tn]) == 'undefined') {
53378 this.toolbars[tn] = this.buildToolbar( false ,tn ,block.friendly_name, block);
53380 this.toolbars[tn].selectedNode = db;
53381 left_label = block.friendly_name;
53382 ans = this.editorcore.getAllAncestors();
53390 if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
53391 return; // no change?
53397 ///console.log("show: " + tn);
53398 this.tb = typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
53402 this.tb.items.first().el.innerHTML = left_label + ': ';
53405 // update attributes
53406 if (block && this.tb.fields) {
53408 this.tb.fields.each(function(e) {
53409 e.setValue(block[e.name]);
53413 } else if (this.tb.fields && this.tb.selectedNode) {
53414 this.tb.fields.each( function(e) {
53416 e.setValue(this.tb.selectedNode.style[e.stylename]);
53419 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
53421 this.updateToolbarStyles(this.tb.selectedNode);
53426 Roo.menu.MenuMgr.hideAll();
53431 // update the footer
53433 this.updateFooter(ans);
53437 updateToolbarStyles : function(sel)
53439 var hasStyles = false;
53440 for(var i in this.styles) {
53446 if (hasStyles && this.tb.hasStyles) {
53447 var st = this.tb.fields.item(0);
53449 st.store.removeAll();
53450 var cn = sel.className.split(/\s+/);
53453 if (this.styles['*']) {
53455 Roo.each(this.styles['*'], function(v) {
53456 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
53459 if (this.styles[tn]) {
53460 Roo.each(this.styles[tn], function(v) {
53461 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
53465 st.store.loadData(avs);
53472 updateFooter : function(ans)
53475 if (ans === false) {
53476 this.footDisp.dom.innerHTML = '';
53480 this.footerEls = ans.reverse();
53481 Roo.each(this.footerEls, function(a,i) {
53482 if (!a) { return; }
53483 html += html.length ? ' > ' : '';
53485 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
53490 var sz = this.footDisp.up('td').getSize();
53491 this.footDisp.dom.style.width = (sz.width -10) + 'px';
53492 this.footDisp.dom.style.marginLeft = '5px';
53494 this.footDisp.dom.style.overflow = 'hidden';
53496 this.footDisp.dom.innerHTML = html;
53503 onDestroy : function(){
53506 this.tb.items.each(function(item){
53508 item.menu.removeAll();
53510 item.menu.el.destroy();
53518 onFirstFocus: function() {
53519 // need to do this for all the toolbars..
53520 this.tb.items.each(function(item){
53524 buildToolbar: function(tlist, nm, friendly_name, block)
53526 var editor = this.editor;
53527 var editorcore = this.editorcore;
53528 // create a new element.
53529 var wdiv = editor.wrap.createChild({
53531 }, editor.wrap.dom.firstChild.nextSibling, true);
53534 var tb = new Roo.Toolbar(wdiv);
53535 ///this.tb = tb; // << this sets the active toolbar..
53536 if (tlist === false && block) {
53537 tlist = block.contextMenu(this);
53540 tb.hasStyles = false;
53543 tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ": ");
53545 var styles = Array.from(this.styles);
53549 if (styles && styles.length) {
53550 tb.hasStyles = true;
53551 // this needs a multi-select checkbox...
53552 tb.addField( new Roo.form.ComboBox({
53553 store: new Roo.data.SimpleStore({
53555 fields: ['val', 'selected'],
53558 name : '-roo-edit-className',
53559 attrname : 'className',
53560 displayField: 'val',
53564 triggerAction: 'all',
53565 emptyText:'Select Style',
53566 selectOnFocus:true,
53569 'select': function(c, r, i) {
53570 // initial support only for on class per el..
53571 tb.selectedNode.className = r ? r.get('val') : '';
53572 editorcore.syncValue();
53579 var tbc = Roo.form.HtmlEditor.ToolbarContext;
53582 for (var i = 0; i < tlist.length; i++) {
53584 // newer versions will use xtype cfg to create menus.
53585 if (typeof(tlist[i].xtype) != 'undefined') {
53587 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
53593 var item = tlist[i];
53594 tb.add(item.title + ": ");
53597 //optname == used so you can configure the options available..
53598 var opts = item.opts ? item.opts : false;
53599 if (item.optname) { // use the b
53600 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
53605 // opts == pulldown..
53606 tb.addField( new Roo.form.ComboBox({
53607 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
53609 fields: ['val', 'display'],
53612 name : '-roo-edit-' + tlist[i].name,
53614 attrname : tlist[i].name,
53615 stylename : item.style ? item.style : false,
53617 displayField: item.displayField ? item.displayField : 'val',
53618 valueField : 'val',
53620 mode: typeof(tbc.stores[tlist[i].name]) != 'undefined' ? 'remote' : 'local',
53622 triggerAction: 'all',
53623 emptyText:'Select',
53624 selectOnFocus:true,
53625 width: item.width ? item.width : 130,
53627 'select': function(c, r, i) {
53631 tb.selectedNode.style[c.stylename] = r.get('val');
53632 editorcore.syncValue();
53636 tb.selectedNode.removeAttribute(c.attrname);
53637 editorcore.syncValue();
53640 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
53641 editorcore.syncValue();
53650 tb.addField( new Roo.form.TextField({
53653 //allowBlank:false,
53659 tb.addField( new Roo.form.TextField({
53660 name: '-roo-edit-' + tlist[i].name,
53661 attrname : tlist[i].name,
53667 'change' : function(f, nv, ov) {
53670 tb.selectedNode.setAttribute(f.attrname, nv);
53671 editorcore.syncValue();
53679 var show_delete = !block || block.deleteTitle !== false;
53681 show_delete = false;
53685 text: 'Stylesheets',
53688 click : function ()
53690 _this.editor.fireEvent('stylesheetsclick', _this.editor);
53699 text: block && block.deleteTitle ? block.deleteTitle : 'Remove Block or Formating', // remove the tag, and puts the children outside...
53702 click : function ()
53704 var sn = tb.selectedNode;
53706 sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
53712 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
53713 if (sn.hasAttribute('data-block')) {
53714 stn = sn.nextSibling || sn.previousSibling || sn.parentNode;
53715 sn.parentNode.removeChild(sn);
53717 } else if (sn && sn.tagName != 'BODY') {
53718 // remove and keep parents.
53719 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
53724 var range = editorcore.createRange();
53726 range.setStart(stn,0);
53727 range.setEnd(stn,0);
53728 var selection = editorcore.getSelection();
53729 selection.removeAllRanges();
53730 selection.addRange(range);
53733 //_this.updateToolbar(null, null, pn);
53734 _this.updateToolbar(null, null, null);
53735 _this.updateFooter(false);
53746 tb.el.on('click', function(e){
53747 e.preventDefault(); // what does this do?
53749 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
53752 // dont need to disable them... as they will get hidden
53757 buildFooter : function()
53760 var fel = this.editor.wrap.createChild();
53761 this.footer = new Roo.Toolbar(fel);
53762 // toolbar has scrolly on left / right?
53763 var footDisp= new Roo.Toolbar.Fill();
53769 handler : function() {
53770 _t.footDisp.scrollTo('left',0,true)
53774 this.footer.add( footDisp );
53779 handler : function() {
53781 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
53785 var fel = Roo.get(footDisp.el);
53786 fel.addClass('x-editor-context');
53787 this.footDispWrap = fel;
53788 this.footDispWrap.overflow = 'hidden';
53790 this.footDisp = fel.createChild();
53791 this.footDispWrap.on('click', this.onContextClick, this)
53795 // when the footer contect changes
53796 onContextClick : function (ev,dom)
53798 ev.preventDefault();
53799 var cn = dom.className;
53801 if (!cn.match(/x-ed-loc-/)) {
53804 var n = cn.split('-').pop();
53805 var ans = this.footerEls;
53808 this.editorcore.selectNode(sel);
53811 this.updateToolbar(null, null, sel);
53828 * Ext JS Library 1.1.1
53829 * Copyright(c) 2006-2007, Ext JS, LLC.
53831 * Originally Released Under LGPL - original licence link has changed is not relivant.
53834 * <script type="text/javascript">
53838 * @class Roo.form.BasicForm
53839 * @extends Roo.util.Observable
53840 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
53842 * @param {String/HTMLElement/Roo.Element} el The form element or its id
53843 * @param {Object} config Configuration options
53845 Roo.form.BasicForm = function(el, config){
53846 this.allItems = [];
53847 this.childForms = [];
53848 Roo.apply(this, config);
53850 * The Roo.form.Field items in this form.
53851 * @type MixedCollection
53855 this.items = new Roo.util.MixedCollection(false, function(o){
53856 return o.id || (o.id = Roo.id());
53860 * @event beforeaction
53861 * Fires before any action is performed. Return false to cancel the action.
53862 * @param {Form} this
53863 * @param {Action} action The action to be performed
53865 beforeaction: true,
53867 * @event actionfailed
53868 * Fires when an action fails.
53869 * @param {Form} this
53870 * @param {Action} action The action that failed
53872 actionfailed : true,
53874 * @event actioncomplete
53875 * Fires when an action is completed.
53876 * @param {Form} this
53877 * @param {Action} action The action that completed
53879 actioncomplete : true
53884 Roo.form.BasicForm.superclass.constructor.call(this);
53886 Roo.form.BasicForm.popover.apply();
53889 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
53891 * @cfg {String} method
53892 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
53895 * @cfg {DataReader} reader
53896 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
53897 * This is optional as there is built-in support for processing JSON.
53900 * @cfg {DataReader} errorReader
53901 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
53902 * This is completely optional as there is built-in support for processing JSON.
53905 * @cfg {String} url
53906 * The URL to use for form actions if one isn't supplied in the action options.
53909 * @cfg {Boolean} fileUpload
53910 * Set to true if this form is a file upload.
53914 * @cfg {Object} baseParams
53915 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
53920 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
53925 activeAction : null,
53928 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
53929 * or setValues() data instead of when the form was first created.
53931 trackResetOnLoad : false,
53935 * childForms - used for multi-tab forms
53938 childForms : false,
53941 * allItems - full list of fields.
53947 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
53948 * element by passing it or its id or mask the form itself by passing in true.
53951 waitMsgTarget : false,
53956 disableMask : false,
53959 * @cfg {Boolean} errorMask (true|false) default false
53964 * @cfg {Number} maskOffset Default 100
53969 initEl : function(el){
53970 this.el = Roo.get(el);
53971 this.id = this.el.id || Roo.id();
53972 this.el.on('submit', this.onSubmit, this);
53973 this.el.addClass('x-form');
53977 onSubmit : function(e){
53982 * Returns true if client-side validation on the form is successful.
53985 isValid : function(){
53987 var target = false;
53988 this.items.each(function(f){
53995 if(!target && f.el.isVisible(true)){
54000 if(this.errorMask && !valid){
54001 Roo.form.BasicForm.popover.mask(this, target);
54007 * Returns array of invalid form fields.
54011 invalidFields : function()
54014 this.items.each(function(f){
54027 * DEPRICATED Returns true if any fields in this form have changed since their original load.
54030 isDirty : function(){
54032 this.items.each(function(f){
54042 * Returns true if any fields in this form have changed since their original load. (New version)
54046 hasChanged : function()
54049 this.items.each(function(f){
54050 if(f.hasChanged()){
54059 * Resets all hasChanged to 'false' -
54060 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
54061 * So hasChanged storage is only to be used for this purpose
54064 resetHasChanged : function()
54066 this.items.each(function(f){
54067 f.resetHasChanged();
54074 * Performs a predefined action (submit or load) or custom actions you define on this form.
54075 * @param {String} actionName The name of the action type
54076 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
54077 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
54078 * accept other config options):
54080 Property Type Description
54081 ---------------- --------------- ----------------------------------------------------------------------------------
54082 url String The url for the action (defaults to the form's url)
54083 method String The form method to use (defaults to the form's method, or POST if not defined)
54084 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
54085 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
54086 validate the form on the client (defaults to false)
54088 * @return {BasicForm} this
54090 doAction : function(action, options){
54091 if(typeof action == 'string'){
54092 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
54094 if(this.fireEvent('beforeaction', this, action) !== false){
54095 this.beforeAction(action);
54096 action.run.defer(100, action);
54102 * Shortcut to do a submit action.
54103 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
54104 * @return {BasicForm} this
54106 submit : function(options){
54107 this.doAction('submit', options);
54112 * Shortcut to do a load action.
54113 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
54114 * @return {BasicForm} this
54116 load : function(options){
54117 this.doAction('load', options);
54122 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
54123 * @param {Record} record The record to edit
54124 * @return {BasicForm} this
54126 updateRecord : function(record){
54127 record.beginEdit();
54128 var fs = record.fields;
54129 fs.each(function(f){
54130 var field = this.findField(f.name);
54132 record.set(f.name, field.getValue());
54140 * Loads an Roo.data.Record into this form.
54141 * @param {Record} record The record to load
54142 * @return {BasicForm} this
54144 loadRecord : function(record){
54145 this.setValues(record.data);
54150 beforeAction : function(action){
54151 var o = action.options;
54153 if(!this.disableMask) {
54154 if(this.waitMsgTarget === true){
54155 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
54156 }else if(this.waitMsgTarget){
54157 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
54158 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
54160 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
54168 afterAction : function(action, success){
54169 this.activeAction = null;
54170 var o = action.options;
54172 if(!this.disableMask) {
54173 if(this.waitMsgTarget === true){
54175 }else if(this.waitMsgTarget){
54176 this.waitMsgTarget.unmask();
54178 Roo.MessageBox.updateProgress(1);
54179 Roo.MessageBox.hide();
54187 Roo.callback(o.success, o.scope, [this, action]);
54188 this.fireEvent('actioncomplete', this, action);
54192 // failure condition..
54193 // we have a scenario where updates need confirming.
54194 // eg. if a locking scenario exists..
54195 // we look for { errors : { needs_confirm : true }} in the response.
54197 (typeof(action.result) != 'undefined') &&
54198 (typeof(action.result.errors) != 'undefined') &&
54199 (typeof(action.result.errors.needs_confirm) != 'undefined')
54202 Roo.MessageBox.confirm(
54203 "Change requires confirmation",
54204 action.result.errorMsg,
54209 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
54219 Roo.callback(o.failure, o.scope, [this, action]);
54220 // show an error message if no failed handler is set..
54221 if (!this.hasListener('actionfailed')) {
54222 Roo.MessageBox.alert("Error",
54223 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
54224 action.result.errorMsg :
54225 "Saving Failed, please check your entries or try again"
54229 this.fireEvent('actionfailed', this, action);
54235 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
54236 * @param {String} id The value to search for
54239 findField : function(id){
54240 var field = this.items.get(id);
54242 this.items.each(function(f){
54243 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
54249 return field || null;
54253 * Add a secondary form to this one,
54254 * Used to provide tabbed forms. One form is primary, with hidden values
54255 * which mirror the elements from the other forms.
54257 * @param {Roo.form.Form} form to add.
54260 addForm : function(form)
54263 if (this.childForms.indexOf(form) > -1) {
54267 this.childForms.push(form);
54269 Roo.each(form.allItems, function (fe) {
54271 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
54272 if (this.findField(n)) { // already added..
54275 var add = new Roo.form.Hidden({
54278 add.render(this.el);
54285 * Mark fields in this form invalid in bulk.
54286 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
54287 * @return {BasicForm} this
54289 markInvalid : function(errors){
54290 if(errors instanceof Array){
54291 for(var i = 0, len = errors.length; i < len; i++){
54292 var fieldError = errors[i];
54293 var f = this.findField(fieldError.id);
54295 f.markInvalid(fieldError.msg);
54301 if(typeof errors[id] != 'function' && (field = this.findField(id))){
54302 field.markInvalid(errors[id]);
54306 Roo.each(this.childForms || [], function (f) {
54307 f.markInvalid(errors);
54314 * Set values for fields in this form in bulk.
54315 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
54316 * @return {BasicForm} this
54318 setValues : function(values){
54319 if(values instanceof Array){ // array of objects
54320 for(var i = 0, len = values.length; i < len; i++){
54322 var f = this.findField(v.id);
54324 f.setValue(v.value);
54325 if(this.trackResetOnLoad){
54326 f.originalValue = f.getValue();
54330 }else{ // object hash
54333 if(typeof values[id] != 'function' && (field = this.findField(id))){
54338 if (field.setFromData &&
54339 field.valueField &&
54340 field.displayField &&
54341 // combos' with local stores can
54342 // be queried via setValue()
54343 // to set their value..
54344 (field.store && !field.store.isLocal)
54348 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
54349 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
54350 field.setFromData(sd);
54352 } else if (field.inputType && field.inputType == 'radio') {
54354 field.setValue(values[id]);
54356 field.setValue(values[id]);
54360 if(this.trackResetOnLoad){
54361 field.originalValue = field.getValue();
54366 this.resetHasChanged();
54369 Roo.each(this.childForms || [], function (f) {
54370 f.setValues(values);
54371 f.resetHasChanged();
54378 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
54379 * they are returned as an array.
54380 * @param {Boolean} asString (def)
54383 getValues : function(asString)
54385 if (this.childForms) {
54386 // copy values from the child forms
54387 Roo.each(this.childForms, function (f) {
54388 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
54393 if (typeof(FormData) != 'undefined' && asString !== true) {
54394 // this relies on a 'recent' version of chrome apparently...
54396 var fd = (new FormData(this.el.dom)).entries();
54398 var ent = fd.next();
54399 while (!ent.done) {
54400 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
54411 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
54412 if(asString === true){
54415 return Roo.urlDecode(fs);
54419 * Returns the fields in this form as an object with key/value pairs.
54420 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
54421 * Normally this will not return readOnly data
54422 * @param {Boolean} with_readonly return readonly field data.
54425 getFieldValues : function(with_readonly)
54427 if (this.childForms) {
54428 // copy values from the child forms
54429 // should this call getFieldValues - probably not as we do not currently copy
54430 // hidden fields when we generate..
54431 Roo.each(this.childForms, function (f) {
54432 this.setValues(f.getFieldValues());
54437 this.items.each(function(f){
54439 if (f.readOnly && with_readonly !== true) {
54440 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
54441 // if a subform contains a copy of them.
54442 // if you have subforms with the same editable data, you will need to copy the data back
54446 if (!f.getName()) {
54449 var v = f.getValue();
54450 if (f.inputType =='radio') {
54451 if (typeof(ret[f.getName()]) == 'undefined') {
54452 ret[f.getName()] = ''; // empty..
54455 if (!f.el.dom.checked) {
54459 v = f.el.dom.value;
54463 // not sure if this supported any more..
54464 if ((typeof(v) == 'object') && f.getRawValue) {
54465 v = f.getRawValue() ; // dates..
54467 // combo boxes where name != hiddenName...
54468 if (f.name != f.getName()) {
54469 ret[f.name] = f.getRawValue();
54471 ret[f.getName()] = v;
54478 * Clears all invalid messages in this form.
54479 * @return {BasicForm} this
54481 clearInvalid : function(){
54482 this.items.each(function(f){
54486 Roo.each(this.childForms || [], function (f) {
54495 * Resets this form.
54496 * @return {BasicForm} this
54498 reset : function(){
54499 this.items.each(function(f){
54503 Roo.each(this.childForms || [], function (f) {
54506 this.resetHasChanged();
54512 * Add Roo.form components to this form.
54513 * @param {Field} field1
54514 * @param {Field} field2 (optional)
54515 * @param {Field} etc (optional)
54516 * @return {BasicForm} this
54519 this.items.addAll(Array.prototype.slice.call(arguments, 0));
54525 * Removes a field from the items collection (does NOT remove its markup).
54526 * @param {Field} field
54527 * @return {BasicForm} this
54529 remove : function(field){
54530 this.items.remove(field);
54535 * Looks at the fields in this form, checks them for an id attribute,
54536 * and calls applyTo on the existing dom element with that id.
54537 * @return {BasicForm} this
54539 render : function(){
54540 this.items.each(function(f){
54541 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
54549 * Calls {@link Ext#apply} for all fields in this form with the passed object.
54550 * @param {Object} values
54551 * @return {BasicForm} this
54553 applyToFields : function(o){
54554 this.items.each(function(f){
54561 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
54562 * @param {Object} values
54563 * @return {BasicForm} this
54565 applyIfToFields : function(o){
54566 this.items.each(function(f){
54574 Roo.BasicForm = Roo.form.BasicForm;
54576 Roo.apply(Roo.form.BasicForm, {
54590 intervalID : false,
54596 if(this.isApplied){
54601 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
54602 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
54603 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
54604 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
54607 this.maskEl.top.enableDisplayMode("block");
54608 this.maskEl.left.enableDisplayMode("block");
54609 this.maskEl.bottom.enableDisplayMode("block");
54610 this.maskEl.right.enableDisplayMode("block");
54612 Roo.get(document.body).on('click', function(){
54616 Roo.get(document.body).on('touchstart', function(){
54620 this.isApplied = true
54623 mask : function(form, target)
54627 this.target = target;
54629 if(!this.form.errorMask || !target.el){
54633 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
54635 var ot = this.target.el.calcOffsetsTo(scrollable);
54637 var scrollTo = ot[1] - this.form.maskOffset;
54639 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
54641 scrollable.scrollTo('top', scrollTo);
54643 var el = this.target.wrap || this.target.el;
54645 var box = el.getBox();
54647 this.maskEl.top.setStyle('position', 'absolute');
54648 this.maskEl.top.setStyle('z-index', 10000);
54649 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
54650 this.maskEl.top.setLeft(0);
54651 this.maskEl.top.setTop(0);
54652 this.maskEl.top.show();
54654 this.maskEl.left.setStyle('position', 'absolute');
54655 this.maskEl.left.setStyle('z-index', 10000);
54656 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
54657 this.maskEl.left.setLeft(0);
54658 this.maskEl.left.setTop(box.y - this.padding);
54659 this.maskEl.left.show();
54661 this.maskEl.bottom.setStyle('position', 'absolute');
54662 this.maskEl.bottom.setStyle('z-index', 10000);
54663 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
54664 this.maskEl.bottom.setLeft(0);
54665 this.maskEl.bottom.setTop(box.bottom + this.padding);
54666 this.maskEl.bottom.show();
54668 this.maskEl.right.setStyle('position', 'absolute');
54669 this.maskEl.right.setStyle('z-index', 10000);
54670 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
54671 this.maskEl.right.setLeft(box.right + this.padding);
54672 this.maskEl.right.setTop(box.y - this.padding);
54673 this.maskEl.right.show();
54675 this.intervalID = window.setInterval(function() {
54676 Roo.form.BasicForm.popover.unmask();
54679 window.onwheel = function(){ return false;};
54681 (function(){ this.isMasked = true; }).defer(500, this);
54685 unmask : function()
54687 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
54691 this.maskEl.top.setStyle('position', 'absolute');
54692 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
54693 this.maskEl.top.hide();
54695 this.maskEl.left.setStyle('position', 'absolute');
54696 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
54697 this.maskEl.left.hide();
54699 this.maskEl.bottom.setStyle('position', 'absolute');
54700 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
54701 this.maskEl.bottom.hide();
54703 this.maskEl.right.setStyle('position', 'absolute');
54704 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
54705 this.maskEl.right.hide();
54707 window.onwheel = function(){ return true;};
54709 if(this.intervalID){
54710 window.clearInterval(this.intervalID);
54711 this.intervalID = false;
54714 this.isMasked = false;
54722 * Ext JS Library 1.1.1
54723 * Copyright(c) 2006-2007, Ext JS, LLC.
54725 * Originally Released Under LGPL - original licence link has changed is not relivant.
54728 * <script type="text/javascript">
54732 * @class Roo.form.Form
54733 * @extends Roo.form.BasicForm
54734 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
54735 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
54737 * @param {Object} config Configuration options
54739 Roo.form.Form = function(config){
54741 if (config.items) {
54742 xitems = config.items;
54743 delete config.items;
54747 Roo.form.Form.superclass.constructor.call(this, null, config);
54748 this.url = this.url || this.action;
54750 this.root = new Roo.form.Layout(Roo.applyIf({
54754 this.active = this.root;
54756 * Array of all the buttons that have been added to this form via {@link addButton}
54760 this.allItems = [];
54763 * @event clientvalidation
54764 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
54765 * @param {Form} this
54766 * @param {Boolean} valid true if the form has passed client-side validation
54768 clientvalidation: true,
54771 * Fires when the form is rendered
54772 * @param {Roo.form.Form} form
54777 if (this.progressUrl) {
54778 // push a hidden field onto the list of fields..
54782 name : 'UPLOAD_IDENTIFIER'
54787 Roo.each(xitems, this.addxtype, this);
54791 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
54793 * @cfg {Roo.Button} buttons[] buttons at bottom of form
54797 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
54800 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
54803 * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
54805 buttonAlign:'center',
54808 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
54813 * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
54814 * This property cascades to child containers if not set.
54819 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
54820 * fires a looping event with that state. This is required to bind buttons to the valid
54821 * state using the config value formBind:true on the button.
54823 monitorValid : false,
54826 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
54831 * @cfg {String} progressUrl - Url to return progress data
54834 progressUrl : false,
54836 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
54837 * sending a formdata with extra parameters - eg uploaded elements.
54843 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
54844 * fields are added and the column is closed. If no fields are passed the column remains open
54845 * until end() is called.
54846 * @param {Object} config The config to pass to the column
54847 * @param {Field} field1 (optional)
54848 * @param {Field} field2 (optional)
54849 * @param {Field} etc (optional)
54850 * @return Column The column container object
54852 column : function(c){
54853 var col = new Roo.form.Column(c);
54855 if(arguments.length > 1){ // duplicate code required because of Opera
54856 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54863 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
54864 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
54865 * until end() is called.
54866 * @param {Object} config The config to pass to the fieldset
54867 * @param {Field} field1 (optional)
54868 * @param {Field} field2 (optional)
54869 * @param {Field} etc (optional)
54870 * @return FieldSet The fieldset container object
54872 fieldset : function(c){
54873 var fs = new Roo.form.FieldSet(c);
54875 if(arguments.length > 1){ // duplicate code required because of Opera
54876 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54883 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
54884 * fields are added and the container is closed. If no fields are passed the container remains open
54885 * until end() is called.
54886 * @param {Object} config The config to pass to the Layout
54887 * @param {Field} field1 (optional)
54888 * @param {Field} field2 (optional)
54889 * @param {Field} etc (optional)
54890 * @return Layout The container object
54892 container : function(c){
54893 var l = new Roo.form.Layout(c);
54895 if(arguments.length > 1){ // duplicate code required because of Opera
54896 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54903 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
54904 * @param {Object} container A Roo.form.Layout or subclass of Layout
54905 * @return {Form} this
54907 start : function(c){
54908 // cascade label info
54909 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
54910 this.active.stack.push(c);
54911 c.ownerCt = this.active;
54917 * Closes the current open container
54918 * @return {Form} this
54921 if(this.active == this.root){
54924 this.active = this.active.ownerCt;
54929 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
54930 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
54931 * as the label of the field.
54932 * @param {Field} field1
54933 * @param {Field} field2 (optional)
54934 * @param {Field} etc. (optional)
54935 * @return {Form} this
54938 this.active.stack.push.apply(this.active.stack, arguments);
54939 this.allItems.push.apply(this.allItems,arguments);
54941 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
54942 if(a[i].isFormField){
54947 Roo.form.Form.superclass.add.apply(this, r);
54957 * Find any element that has been added to a form, using it's ID or name
54958 * This can include framesets, columns etc. along with regular fields..
54959 * @param {String} id - id or name to find.
54961 * @return {Element} e - or false if nothing found.
54963 findbyId : function(id)
54969 Roo.each(this.allItems, function(f){
54970 if (f.id == id || f.name == id ){
54981 * Render this form into the passed container. This should only be called once!
54982 * @param {String/HTMLElement/Element} container The element this component should be rendered into
54983 * @return {Form} this
54985 render : function(ct)
54991 var o = this.autoCreate || {
54993 method : this.method || 'POST',
54994 id : this.id || Roo.id()
54996 this.initEl(ct.createChild(o));
54998 this.root.render(this.el);
55002 this.items.each(function(f){
55003 f.render('x-form-el-'+f.id);
55006 if(this.buttons.length > 0){
55007 // tables are required to maintain order and for correct IE layout
55008 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
55009 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
55010 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
55012 var tr = tb.getElementsByTagName('tr')[0];
55013 for(var i = 0, len = this.buttons.length; i < len; i++) {
55014 var b = this.buttons[i];
55015 var td = document.createElement('td');
55016 td.className = 'x-form-btn-td';
55017 b.render(tr.appendChild(td));
55020 if(this.monitorValid){ // initialize after render
55021 this.startMonitoring();
55023 this.fireEvent('rendered', this);
55028 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
55029 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
55030 * object or a valid Roo.DomHelper element config
55031 * @param {Function} handler The function called when the button is clicked
55032 * @param {Object} scope (optional) The scope of the handler function
55033 * @return {Roo.Button}
55035 addButton : function(config, handler, scope){
55039 minWidth: this.minButtonWidth,
55042 if(typeof config == "string"){
55045 Roo.apply(bc, config);
55047 var btn = new Roo.Button(null, bc);
55048 this.buttons.push(btn);
55053 * Adds a series of form elements (using the xtype property as the factory method.
55054 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
55055 * @param {Object} config
55058 addxtype : function()
55060 var ar = Array.prototype.slice.call(arguments, 0);
55062 for(var i = 0; i < ar.length; i++) {
55064 continue; // skip -- if this happends something invalid got sent, we
55065 // should ignore it, as basically that interface element will not show up
55066 // and that should be pretty obvious!!
55069 if (Roo.form[ar[i].xtype]) {
55071 var fe = Roo.factory(ar[i], Roo.form);
55077 fe.store.form = this;
55082 this.allItems.push(fe);
55083 if (fe.items && fe.addxtype) {
55084 fe.addxtype.apply(fe, fe.items);
55094 // console.log('adding ' + ar[i].xtype);
55096 if (ar[i].xtype == 'Button') {
55097 //console.log('adding button');
55098 //console.log(ar[i]);
55099 this.addButton(ar[i]);
55100 this.allItems.push(fe);
55104 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
55105 alert('end is not supported on xtype any more, use items');
55107 // //console.log('adding end');
55115 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
55116 * option "monitorValid"
55118 startMonitoring : function(){
55121 Roo.TaskMgr.start({
55122 run : this.bindHandler,
55123 interval : this.monitorPoll || 200,
55130 * Stops monitoring of the valid state of this form
55132 stopMonitoring : function(){
55133 this.bound = false;
55137 bindHandler : function(){
55139 return false; // stops binding
55142 this.items.each(function(f){
55143 if(!f.isValid(true)){
55148 for(var i = 0, len = this.buttons.length; i < len; i++){
55149 var btn = this.buttons[i];
55150 if(btn.formBind === true && btn.disabled === valid){
55151 btn.setDisabled(!valid);
55154 this.fireEvent('clientvalidation', this, valid);
55168 Roo.Form = Roo.form.Form;
55171 * Ext JS Library 1.1.1
55172 * Copyright(c) 2006-2007, Ext JS, LLC.
55174 * Originally Released Under LGPL - original licence link has changed is not relivant.
55177 * <script type="text/javascript">
55180 // as we use this in bootstrap.
55181 Roo.namespace('Roo.form');
55183 * @class Roo.form.Action
55184 * Internal Class used to handle form actions
55186 * @param {Roo.form.BasicForm} el The form element or its id
55187 * @param {Object} config Configuration options
55192 // define the action interface
55193 Roo.form.Action = function(form, options){
55195 this.options = options || {};
55198 * Client Validation Failed
55201 Roo.form.Action.CLIENT_INVALID = 'client';
55203 * Server Validation Failed
55206 Roo.form.Action.SERVER_INVALID = 'server';
55208 * Connect to Server Failed
55211 Roo.form.Action.CONNECT_FAILURE = 'connect';
55213 * Reading Data from Server Failed
55216 Roo.form.Action.LOAD_FAILURE = 'load';
55218 Roo.form.Action.prototype = {
55220 failureType : undefined,
55221 response : undefined,
55222 result : undefined,
55224 // interface method
55225 run : function(options){
55229 // interface method
55230 success : function(response){
55234 // interface method
55235 handleResponse : function(response){
55239 // default connection failure
55240 failure : function(response){
55242 this.response = response;
55243 this.failureType = Roo.form.Action.CONNECT_FAILURE;
55244 this.form.afterAction(this, false);
55247 processResponse : function(response){
55248 this.response = response;
55249 if(!response.responseText){
55252 this.result = this.handleResponse(response);
55253 return this.result;
55256 // utility functions used internally
55257 getUrl : function(appendParams){
55258 var url = this.options.url || this.form.url || this.form.el.dom.action;
55260 var p = this.getParams();
55262 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
55268 getMethod : function(){
55269 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
55272 getParams : function(){
55273 var bp = this.form.baseParams;
55274 var p = this.options.params;
55276 if(typeof p == "object"){
55277 p = Roo.urlEncode(Roo.applyIf(p, bp));
55278 }else if(typeof p == 'string' && bp){
55279 p += '&' + Roo.urlEncode(bp);
55282 p = Roo.urlEncode(bp);
55287 createCallback : function(){
55289 success: this.success,
55290 failure: this.failure,
55292 timeout: (this.form.timeout*1000),
55293 upload: this.form.fileUpload ? this.success : undefined
55298 Roo.form.Action.Submit = function(form, options){
55299 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
55302 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
55305 haveProgress : false,
55306 uploadComplete : false,
55308 // uploadProgress indicator.
55309 uploadProgress : function()
55311 if (!this.form.progressUrl) {
55315 if (!this.haveProgress) {
55316 Roo.MessageBox.progress("Uploading", "Uploading");
55318 if (this.uploadComplete) {
55319 Roo.MessageBox.hide();
55323 this.haveProgress = true;
55325 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
55327 var c = new Roo.data.Connection();
55329 url : this.form.progressUrl,
55334 success : function(req){
55335 //console.log(data);
55339 rdata = Roo.decode(req.responseText)
55341 Roo.log("Invalid data from server..");
55345 if (!rdata || !rdata.success) {
55347 Roo.MessageBox.alert(Roo.encode(rdata));
55350 var data = rdata.data;
55352 if (this.uploadComplete) {
55353 Roo.MessageBox.hide();
55358 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
55359 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
55362 this.uploadProgress.defer(2000,this);
55365 failure: function(data) {
55366 Roo.log('progress url failed ');
55377 // run get Values on the form, so it syncs any secondary forms.
55378 this.form.getValues();
55380 var o = this.options;
55381 var method = this.getMethod();
55382 var isPost = method == 'POST';
55383 if(o.clientValidation === false || this.form.isValid()){
55385 if (this.form.progressUrl) {
55386 this.form.findField('UPLOAD_IDENTIFIER').setValue(
55387 (new Date() * 1) + '' + Math.random());
55392 Roo.Ajax.request(Roo.apply(this.createCallback(), {
55393 form:this.form.el.dom,
55394 url:this.getUrl(!isPost),
55396 params:isPost ? this.getParams() : null,
55397 isUpload: this.form.fileUpload,
55398 formData : this.form.formData
55401 this.uploadProgress();
55403 }else if (o.clientValidation !== false){ // client validation failed
55404 this.failureType = Roo.form.Action.CLIENT_INVALID;
55405 this.form.afterAction(this, false);
55409 success : function(response)
55411 this.uploadComplete= true;
55412 if (this.haveProgress) {
55413 Roo.MessageBox.hide();
55417 var result = this.processResponse(response);
55418 if(result === true || result.success){
55419 this.form.afterAction(this, true);
55423 this.form.markInvalid(result.errors);
55424 this.failureType = Roo.form.Action.SERVER_INVALID;
55426 this.form.afterAction(this, false);
55428 failure : function(response)
55430 this.uploadComplete= true;
55431 if (this.haveProgress) {
55432 Roo.MessageBox.hide();
55435 this.response = response;
55436 this.failureType = Roo.form.Action.CONNECT_FAILURE;
55437 this.form.afterAction(this, false);
55440 handleResponse : function(response){
55441 if(this.form.errorReader){
55442 var rs = this.form.errorReader.read(response);
55445 for(var i = 0, len = rs.records.length; i < len; i++) {
55446 var r = rs.records[i];
55447 errors[i] = r.data;
55450 if(errors.length < 1){
55454 success : rs.success,
55460 var rt = response.responseText;
55461 if (rt.match(/^\<!--\[CDATA\[/)) {
55462 rt = rt.replace(/^\<!--\[CDATA\[/,'');
55463 rt = rt.replace(/\]\]--\>$/,'');
55466 ret = Roo.decode(rt);
55470 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
55480 Roo.form.Action.Load = function(form, options){
55481 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
55482 this.reader = this.form.reader;
55485 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
55490 Roo.Ajax.request(Roo.apply(
55491 this.createCallback(), {
55492 method:this.getMethod(),
55493 url:this.getUrl(false),
55494 params:this.getParams()
55498 success : function(response){
55500 var result = this.processResponse(response);
55501 if(result === true || !result.success || !result.data){
55502 this.failureType = Roo.form.Action.LOAD_FAILURE;
55503 this.form.afterAction(this, false);
55506 this.form.clearInvalid();
55507 this.form.setValues(result.data);
55508 this.form.afterAction(this, true);
55511 handleResponse : function(response){
55512 if(this.form.reader){
55513 var rs = this.form.reader.read(response);
55514 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
55516 success : rs.success,
55520 return Roo.decode(response.responseText);
55524 Roo.form.Action.ACTION_TYPES = {
55525 'load' : Roo.form.Action.Load,
55526 'submit' : Roo.form.Action.Submit
55529 * Ext JS Library 1.1.1
55530 * Copyright(c) 2006-2007, Ext JS, LLC.
55532 * Originally Released Under LGPL - original licence link has changed is not relivant.
55535 * <script type="text/javascript">
55539 * @class Roo.form.Layout
55540 * @extends Roo.Component
55541 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55542 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
55544 * @param {Object} config Configuration options
55546 Roo.form.Layout = function(config){
55548 if (config.items) {
55549 xitems = config.items;
55550 delete config.items;
55552 Roo.form.Layout.superclass.constructor.call(this, config);
55554 Roo.each(xitems, this.addxtype, this);
55558 Roo.extend(Roo.form.Layout, Roo.Component, {
55560 * @cfg {String/Object} autoCreate
55561 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
55564 * @cfg {String/Object/Function} style
55565 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
55566 * a function which returns such a specification.
55569 * @cfg {String} labelAlign (left|top|right)
55570 * Valid values are "left," "top" and "right" (defaults to "left")
55573 * @cfg {Number} labelWidth
55574 * Fixed width in pixels of all field labels (defaults to undefined)
55577 * @cfg {Boolean} clear
55578 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
55582 * @cfg {String} labelSeparator
55583 * The separator to use after field labels (defaults to ':')
55585 labelSeparator : ':',
55587 * @cfg {Boolean} hideLabels
55588 * True to suppress the display of field labels in this layout (defaults to false)
55590 hideLabels : false,
55593 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
55598 onRender : function(ct, position){
55599 if(this.el){ // from markup
55600 this.el = Roo.get(this.el);
55601 }else { // generate
55602 var cfg = this.getAutoCreate();
55603 this.el = ct.createChild(cfg, position);
55606 this.el.applyStyles(this.style);
55608 if(this.labelAlign){
55609 this.el.addClass('x-form-label-'+this.labelAlign);
55611 if(this.hideLabels){
55612 this.labelStyle = "display:none";
55613 this.elementStyle = "padding-left:0;";
55615 if(typeof this.labelWidth == 'number'){
55616 this.labelStyle = "width:"+this.labelWidth+"px;";
55617 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
55619 if(this.labelAlign == 'top'){
55620 this.labelStyle = "width:auto;";
55621 this.elementStyle = "padding-left:0;";
55624 var stack = this.stack;
55625 var slen = stack.length;
55627 if(!this.fieldTpl){
55628 var t = new Roo.Template(
55629 '<div class="x-form-item {5}">',
55630 '<label for="{0}" style="{2}">{1}{4}</label>',
55631 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55633 '</div><div class="x-form-clear-left"></div>'
55635 t.disableFormats = true;
55637 Roo.form.Layout.prototype.fieldTpl = t;
55639 for(var i = 0; i < slen; i++) {
55640 if(stack[i].isFormField){
55641 this.renderField(stack[i]);
55643 this.renderComponent(stack[i]);
55648 this.el.createChild({cls:'x-form-clear'});
55653 renderField : function(f){
55654 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
55657 f.labelStyle||this.labelStyle||'', //2
55658 this.elementStyle||'', //3
55659 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
55660 f.itemCls||this.itemCls||'' //5
55661 ], true).getPrevSibling());
55665 renderComponent : function(c){
55666 c.render(c.isLayout ? this.el : this.el.createChild());
55669 * Adds a object form elements (using the xtype property as the factory method.)
55670 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
55671 * @param {Object} config
55673 addxtype : function(o)
55675 // create the lement.
55676 o.form = this.form;
55677 var fe = Roo.factory(o, Roo.form);
55678 this.form.allItems.push(fe);
55679 this.stack.push(fe);
55681 if (fe.isFormField) {
55682 this.form.items.add(fe);
55691 * @class Roo.form.Column
55692 * @extends Roo.form.Layout
55693 * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55694 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
55696 * @param {Object} config Configuration options
55698 Roo.form.Column = function(config){
55699 Roo.form.Column.superclass.constructor.call(this, config);
55702 Roo.extend(Roo.form.Column, Roo.form.Layout, {
55704 * @cfg {Number/String} width
55705 * The fixed width of the column in pixels or CSS value (defaults to "auto")
55708 * @cfg {String/Object} autoCreate
55709 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
55713 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
55716 onRender : function(ct, position){
55717 Roo.form.Column.superclass.onRender.call(this, ct, position);
55719 this.el.setWidth(this.width);
55725 * @class Roo.form.Row
55726 * @extends Roo.form.Layout
55727 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55728 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
55730 * @param {Object} config Configuration options
55734 Roo.form.Row = function(config){
55735 Roo.form.Row.superclass.constructor.call(this, config);
55738 Roo.extend(Roo.form.Row, Roo.form.Layout, {
55740 * @cfg {Number/String} width
55741 * The fixed width of the column in pixels or CSS value (defaults to "auto")
55744 * @cfg {Number/String} height
55745 * The fixed height of the column in pixels or CSS value (defaults to "auto")
55747 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
55751 onRender : function(ct, position){
55752 //console.log('row render');
55754 var t = new Roo.Template(
55755 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
55756 '<label for="{0}" style="{2}">{1}{4}</label>',
55757 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55761 t.disableFormats = true;
55763 Roo.form.Layout.prototype.rowTpl = t;
55765 this.fieldTpl = this.rowTpl;
55767 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
55768 var labelWidth = 100;
55770 if ((this.labelAlign != 'top')) {
55771 if (typeof this.labelWidth == 'number') {
55772 labelWidth = this.labelWidth
55774 this.padWidth = 20 + labelWidth;
55778 Roo.form.Column.superclass.onRender.call(this, ct, position);
55780 this.el.setWidth(this.width);
55783 this.el.setHeight(this.height);
55788 renderField : function(f){
55789 f.fieldEl = this.fieldTpl.append(this.el, [
55790 f.id, f.fieldLabel,
55791 f.labelStyle||this.labelStyle||'',
55792 this.elementStyle||'',
55793 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
55794 f.itemCls||this.itemCls||'',
55795 f.width ? f.width + this.padWidth : 160 + this.padWidth
55802 * @class Roo.form.FieldSet
55803 * @extends Roo.form.Layout
55804 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
55805 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
55807 * @param {Object} config Configuration options
55809 Roo.form.FieldSet = function(config){
55810 Roo.form.FieldSet.superclass.constructor.call(this, config);
55813 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
55815 * @cfg {String} legend
55816 * The text to display as the legend for the FieldSet (defaults to '')
55819 * @cfg {String/Object} autoCreate
55820 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
55824 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
55827 onRender : function(ct, position){
55828 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
55830 this.setLegend(this.legend);
55835 setLegend : function(text){
55837 this.el.child('legend').update(text);
55842 * Ext JS Library 1.1.1
55843 * Copyright(c) 2006-2007, Ext JS, LLC.
55845 * Originally Released Under LGPL - original licence link has changed is not relivant.
55848 * <script type="text/javascript">
55851 * @class Roo.form.VTypes
55852 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
55855 Roo.form.VTypes = function(){
55856 // closure these in so they are only created once.
55857 var alpha = /^[a-zA-Z_]+$/;
55858 var alphanum = /^[a-zA-Z0-9_]+$/;
55859 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
55860 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
55862 // All these messages and functions are configurable
55865 * The function used to validate email addresses
55866 * @param {String} value The email address
55868 email : function(v){
55869 return email.test(v);
55872 * The error text to display when the email validation function returns false
55875 emailText : 'This field should be an e-mail address in the format "user@domain.com"',
55877 * The keystroke filter mask to be applied on email input
55880 emailMask : /[a-z0-9_\.\-@]/i,
55883 * The function used to validate URLs
55884 * @param {String} value The URL
55887 return url.test(v);
55890 * The error text to display when the url validation function returns false
55893 urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
55896 * The function used to validate alpha values
55897 * @param {String} value The value
55899 alpha : function(v){
55900 return alpha.test(v);
55903 * The error text to display when the alpha validation function returns false
55906 alphaText : 'This field should only contain letters and _',
55908 * The keystroke filter mask to be applied on alpha input
55911 alphaMask : /[a-z_]/i,
55914 * The function used to validate alphanumeric values
55915 * @param {String} value The value
55917 alphanum : function(v){
55918 return alphanum.test(v);
55921 * The error text to display when the alphanumeric validation function returns false
55924 alphanumText : 'This field should only contain letters, numbers and _',
55926 * The keystroke filter mask to be applied on alphanumeric input
55929 alphanumMask : /[a-z0-9_]/i
55931 }();//<script type="text/javascript">
55934 * @class Roo.form.FCKeditor
55935 * @extends Roo.form.TextArea
55936 * Wrapper around the FCKEditor http://www.fckeditor.net
55938 * Creates a new FCKeditor
55939 * @param {Object} config Configuration options
55941 Roo.form.FCKeditor = function(config){
55942 Roo.form.FCKeditor.superclass.constructor.call(this, config);
55945 * @event editorinit
55946 * Fired when the editor is initialized - you can add extra handlers here..
55947 * @param {FCKeditor} this
55948 * @param {Object} the FCK object.
55955 Roo.form.FCKeditor.editors = { };
55956 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
55958 //defaultAutoCreate : {
55959 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
55963 * @cfg {Object} fck options - see fck manual for details.
55968 * @cfg {Object} fck toolbar set (Basic or Default)
55970 toolbarSet : 'Basic',
55972 * @cfg {Object} fck BasePath
55974 basePath : '/fckeditor/',
55982 onRender : function(ct, position)
55985 this.defaultAutoCreate = {
55987 style:"width:300px;height:60px;",
55988 autocomplete: "new-password"
55991 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
55994 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
55995 if(this.preventScrollbars){
55996 this.el.setStyle("overflow", "hidden");
55998 this.el.setHeight(this.growMin);
56001 //console.log('onrender' + this.getId() );
56002 Roo.form.FCKeditor.editors[this.getId()] = this;
56005 this.replaceTextarea() ;
56009 getEditor : function() {
56010 return this.fckEditor;
56013 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
56014 * @param {Mixed} value The value to set
56018 setValue : function(value)
56020 //console.log('setValue: ' + value);
56022 if(typeof(value) == 'undefined') { // not sure why this is happending...
56025 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
56027 //if(!this.el || !this.getEditor()) {
56028 // this.value = value;
56029 //this.setValue.defer(100,this,[value]);
56033 if(!this.getEditor()) {
56037 this.getEditor().SetData(value);
56044 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
56045 * @return {Mixed} value The field value
56047 getValue : function()
56050 if (this.frame && this.frame.dom.style.display == 'none') {
56051 return Roo.form.FCKeditor.superclass.getValue.call(this);
56054 if(!this.el || !this.getEditor()) {
56056 // this.getValue.defer(100,this);
56061 var value=this.getEditor().GetData();
56062 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
56063 return Roo.form.FCKeditor.superclass.getValue.call(this);
56069 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
56070 * @return {Mixed} value The field value
56072 getRawValue : function()
56074 if (this.frame && this.frame.dom.style.display == 'none') {
56075 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
56078 if(!this.el || !this.getEditor()) {
56079 //this.getRawValue.defer(100,this);
56086 var value=this.getEditor().GetData();
56087 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
56088 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
56092 setSize : function(w,h) {
56096 //if (this.frame && this.frame.dom.style.display == 'none') {
56097 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
56100 //if(!this.el || !this.getEditor()) {
56101 // this.setSize.defer(100,this, [w,h]);
56107 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
56109 this.frame.dom.setAttribute('width', w);
56110 this.frame.dom.setAttribute('height', h);
56111 this.frame.setSize(w,h);
56115 toggleSourceEdit : function(value) {
56119 this.el.dom.style.display = value ? '' : 'none';
56120 this.frame.dom.style.display = value ? 'none' : '';
56125 focus: function(tag)
56127 if (this.frame.dom.style.display == 'none') {
56128 return Roo.form.FCKeditor.superclass.focus.call(this);
56130 if(!this.el || !this.getEditor()) {
56131 this.focus.defer(100,this, [tag]);
56138 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
56139 this.getEditor().Focus();
56141 if (!this.getEditor().Selection.GetSelection()) {
56142 this.focus.defer(100,this, [tag]);
56147 var r = this.getEditor().EditorDocument.createRange();
56148 r.setStart(tgs[0],0);
56149 r.setEnd(tgs[0],0);
56150 this.getEditor().Selection.GetSelection().removeAllRanges();
56151 this.getEditor().Selection.GetSelection().addRange(r);
56152 this.getEditor().Focus();
56159 replaceTextarea : function()
56161 if ( document.getElementById( this.getId() + '___Frame' ) ) {
56164 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
56166 // We must check the elements firstly using the Id and then the name.
56167 var oTextarea = document.getElementById( this.getId() );
56169 var colElementsByName = document.getElementsByName( this.getId() ) ;
56171 oTextarea.style.display = 'none' ;
56173 if ( oTextarea.tabIndex ) {
56174 this.TabIndex = oTextarea.tabIndex ;
56177 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
56178 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
56179 this.frame = Roo.get(this.getId() + '___Frame')
56182 _getConfigHtml : function()
56186 for ( var o in this.fckconfig ) {
56187 sConfig += sConfig.length > 0 ? '&' : '';
56188 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
56191 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
56195 _getIFrameHtml : function()
56197 var sFile = 'fckeditor.html' ;
56198 /* no idea what this is about..
56201 if ( (/fcksource=true/i).test( window.top.location.search ) )
56202 sFile = 'fckeditor.original.html' ;
56207 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
56208 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
56211 var html = '<iframe id="' + this.getId() +
56212 '___Frame" src="' + sLink +
56213 '" width="' + this.width +
56214 '" height="' + this.height + '"' +
56215 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
56216 ' frameborder="0" scrolling="no"></iframe>' ;
56221 _insertHtmlBefore : function( html, element )
56223 if ( element.insertAdjacentHTML ) {
56225 element.insertAdjacentHTML( 'beforeBegin', html ) ;
56227 var oRange = document.createRange() ;
56228 oRange.setStartBefore( element ) ;
56229 var oFragment = oRange.createContextualFragment( html );
56230 element.parentNode.insertBefore( oFragment, element ) ;
56243 //Roo.reg('fckeditor', Roo.form.FCKeditor);
56245 function FCKeditor_OnComplete(editorInstance){
56246 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
56247 f.fckEditor = editorInstance;
56248 //console.log("loaded");
56249 f.fireEvent('editorinit', f, editorInstance);
56269 //<script type="text/javascript">
56271 * @class Roo.form.GridField
56272 * @extends Roo.form.Field
56273 * Embed a grid (or editable grid into a form)
56276 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
56278 * xgrid.store = Roo.data.Store
56279 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
56280 * xgrid.store.reader = Roo.data.JsonReader
56284 * Creates a new GridField
56285 * @param {Object} config Configuration options
56287 Roo.form.GridField = function(config){
56288 Roo.form.GridField.superclass.constructor.call(this, config);
56292 Roo.extend(Roo.form.GridField, Roo.form.Field, {
56294 * @cfg {Number} width - used to restrict width of grid..
56298 * @cfg {Number} height - used to restrict height of grid..
56302 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
56308 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56309 * {tag: "input", type: "checkbox", autocomplete: "off"})
56311 // defaultAutoCreate : { tag: 'div' },
56312 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
56314 * @cfg {String} addTitle Text to include for adding a title.
56318 onResize : function(){
56319 Roo.form.Field.superclass.onResize.apply(this, arguments);
56322 initEvents : function(){
56323 // Roo.form.Checkbox.superclass.initEvents.call(this);
56324 // has no events...
56329 getResizeEl : function(){
56333 getPositionEl : function(){
56338 onRender : function(ct, position){
56340 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
56341 var style = this.style;
56344 Roo.form.GridField.superclass.onRender.call(this, ct, position);
56345 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
56346 this.viewEl = this.wrap.createChild({ tag: 'div' });
56348 this.viewEl.applyStyles(style);
56351 this.viewEl.setWidth(this.width);
56354 this.viewEl.setHeight(this.height);
56356 //if(this.inputValue !== undefined){
56357 //this.setValue(this.value);
56360 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
56363 this.grid.render();
56364 this.grid.getDataSource().on('remove', this.refreshValue, this);
56365 this.grid.getDataSource().on('update', this.refreshValue, this);
56366 this.grid.on('afteredit', this.refreshValue, this);
56372 * Sets the value of the item.
56373 * @param {String} either an object or a string..
56375 setValue : function(v){
56377 v = v || []; // empty set..
56378 // this does not seem smart - it really only affects memoryproxy grids..
56379 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
56380 var ds = this.grid.getDataSource();
56381 // assumes a json reader..
56383 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
56384 ds.loadData( data);
56386 // clear selection so it does not get stale.
56387 if (this.grid.sm) {
56388 this.grid.sm.clearSelections();
56391 Roo.form.GridField.superclass.setValue.call(this, v);
56392 this.refreshValue();
56393 // should load data in the grid really....
56397 refreshValue: function() {
56399 this.grid.getDataSource().each(function(r) {
56402 this.el.dom.value = Roo.encode(val);
56410 * Ext JS Library 1.1.1
56411 * Copyright(c) 2006-2007, Ext JS, LLC.
56413 * Originally Released Under LGPL - original licence link has changed is not relivant.
56416 * <script type="text/javascript">
56419 * @class Roo.form.DisplayField
56420 * @extends Roo.form.Field
56421 * A generic Field to display non-editable data.
56422 * @cfg {Boolean} closable (true|false) default false
56424 * Creates a new Display Field item.
56425 * @param {Object} config Configuration options
56427 Roo.form.DisplayField = function(config){
56428 Roo.form.DisplayField.superclass.constructor.call(this, config);
56433 * Fires after the click the close btn
56434 * @param {Roo.form.DisplayField} this
56440 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
56441 inputType: 'hidden',
56447 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56449 focusClass : undefined,
56451 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56453 fieldClass: 'x-form-field',
56456 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
56458 valueRenderer: undefined,
56462 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56463 * {tag: "input", type: "checkbox", autocomplete: "off"})
56466 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
56470 onResize : function(){
56471 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
56475 initEvents : function(){
56476 // Roo.form.Checkbox.superclass.initEvents.call(this);
56477 // has no events...
56480 this.closeEl.on('click', this.onClose, this);
56486 getResizeEl : function(){
56490 getPositionEl : function(){
56495 onRender : function(ct, position){
56497 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
56498 //if(this.inputValue !== undefined){
56499 this.wrap = this.el.wrap();
56501 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
56504 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
56507 if (this.bodyStyle) {
56508 this.viewEl.applyStyles(this.bodyStyle);
56510 //this.viewEl.setStyle('padding', '2px');
56512 this.setValue(this.value);
56517 initValue : Roo.emptyFn,
56522 onClick : function(){
56527 * Sets the checked state of the checkbox.
56528 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
56530 setValue : function(v){
56532 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
56533 // this might be called before we have a dom element..
56534 if (!this.viewEl) {
56537 this.viewEl.dom.innerHTML = html;
56538 Roo.form.DisplayField.superclass.setValue.call(this, v);
56542 onClose : function(e)
56544 e.preventDefault();
56546 this.fireEvent('close', this);
56555 * @class Roo.form.DayPicker
56556 * @extends Roo.form.Field
56557 * A Day picker show [M] [T] [W] ....
56559 * Creates a new Day Picker
56560 * @param {Object} config Configuration options
56562 Roo.form.DayPicker= function(config){
56563 Roo.form.DayPicker.superclass.constructor.call(this, config);
56567 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
56569 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56571 focusClass : undefined,
56573 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56575 fieldClass: "x-form-field",
56578 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56579 * {tag: "input", type: "checkbox", autocomplete: "off"})
56581 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
56584 actionMode : 'viewEl',
56588 inputType : 'hidden',
56591 inputElement: false, // real input element?
56592 basedOn: false, // ????
56594 isFormField: true, // not sure where this is needed!!!!
56596 onResize : function(){
56597 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
56598 if(!this.boxLabel){
56599 this.el.alignTo(this.wrap, 'c-c');
56603 initEvents : function(){
56604 Roo.form.Checkbox.superclass.initEvents.call(this);
56605 this.el.on("click", this.onClick, this);
56606 this.el.on("change", this.onClick, this);
56610 getResizeEl : function(){
56614 getPositionEl : function(){
56620 onRender : function(ct, position){
56621 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
56623 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
56625 var r1 = '<table><tr>';
56626 var r2 = '<tr class="x-form-daypick-icons">';
56627 for (var i=0; i < 7; i++) {
56628 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
56629 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
56632 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
56633 viewEl.select('img').on('click', this.onClick, this);
56634 this.viewEl = viewEl;
56637 // this will not work on Chrome!!!
56638 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
56639 this.el.on('propertychange', this.setFromHidden, this); //ie
56647 initValue : Roo.emptyFn,
56650 * Returns the checked state of the checkbox.
56651 * @return {Boolean} True if checked, else false
56653 getValue : function(){
56654 return this.el.dom.value;
56659 onClick : function(e){
56660 //this.setChecked(!this.checked);
56661 Roo.get(e.target).toggleClass('x-menu-item-checked');
56662 this.refreshValue();
56663 //if(this.el.dom.checked != this.checked){
56664 // this.setValue(this.el.dom.checked);
56669 refreshValue : function()
56672 this.viewEl.select('img',true).each(function(e,i,n) {
56673 val += e.is(".x-menu-item-checked") ? String(n) : '';
56675 this.setValue(val, true);
56679 * Sets the checked state of the checkbox.
56680 * On is always based on a string comparison between inputValue and the param.
56681 * @param {Boolean/String} value - the value to set
56682 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
56684 setValue : function(v,suppressEvent){
56685 if (!this.el.dom) {
56688 var old = this.el.dom.value ;
56689 this.el.dom.value = v;
56690 if (suppressEvent) {
56694 // update display..
56695 this.viewEl.select('img',true).each(function(e,i,n) {
56697 var on = e.is(".x-menu-item-checked");
56698 var newv = v.indexOf(String(n)) > -1;
56700 e.toggleClass('x-menu-item-checked');
56706 this.fireEvent('change', this, v, old);
56711 // handle setting of hidden value by some other method!!?!?
56712 setFromHidden: function()
56717 //console.log("SET FROM HIDDEN");
56718 //alert('setFrom hidden');
56719 this.setValue(this.el.dom.value);
56722 onDestroy : function()
56725 Roo.get(this.viewEl).remove();
56728 Roo.form.DayPicker.superclass.onDestroy.call(this);
56732 * RooJS Library 1.1.1
56733 * Copyright(c) 2008-2011 Alan Knowles
56740 * @class Roo.form.ComboCheck
56741 * @extends Roo.form.ComboBox
56742 * A combobox for multiple select items.
56744 * FIXME - could do with a reset button..
56747 * Create a new ComboCheck
56748 * @param {Object} config Configuration options
56750 Roo.form.ComboCheck = function(config){
56751 Roo.form.ComboCheck.superclass.constructor.call(this, config);
56752 // should verify some data...
56754 // hiddenName = required..
56755 // displayField = required
56756 // valudField == required
56757 var req= [ 'hiddenName', 'displayField', 'valueField' ];
56759 Roo.each(req, function(e) {
56760 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
56761 throw "Roo.form.ComboCheck : missing value for: " + e;
56768 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
56773 selectedClass: 'x-menu-item-checked',
56776 onRender : function(ct, position){
56782 var cls = 'x-combo-list';
56785 this.tpl = new Roo.Template({
56786 html : '<div class="'+cls+'-item x-menu-check-item">' +
56787 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
56788 '<span>{' + this.displayField + '}</span>' +
56795 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
56796 this.view.singleSelect = false;
56797 this.view.multiSelect = true;
56798 this.view.toggleSelect = true;
56799 this.pageTb.add(new Roo.Toolbar.Fill(), {
56802 handler: function()
56809 onViewOver : function(e, t){
56815 onViewClick : function(doFocus,index){
56819 select: function () {
56820 //Roo.log("SELECT CALLED");
56823 selectByValue : function(xv, scrollIntoView){
56824 var ar = this.getValueArray();
56827 Roo.each(ar, function(v) {
56828 if(v === undefined || v === null){
56831 var r = this.findRecord(this.valueField, v);
56833 sels.push(this.store.indexOf(r))
56837 this.view.select(sels);
56843 onSelect : function(record, index){
56844 // Roo.log("onselect Called");
56845 // this is only called by the clear button now..
56846 this.view.clearSelections();
56847 this.setValue('[]');
56848 if (this.value != this.valueBefore) {
56849 this.fireEvent('change', this, this.value, this.valueBefore);
56850 this.valueBefore = this.value;
56853 getValueArray : function()
56858 //Roo.log(this.value);
56859 if (typeof(this.value) == 'undefined') {
56862 var ar = Roo.decode(this.value);
56863 return ar instanceof Array ? ar : []; //?? valid?
56866 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
56871 expand : function ()
56874 Roo.form.ComboCheck.superclass.expand.call(this);
56875 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
56876 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
56881 collapse : function(){
56882 Roo.form.ComboCheck.superclass.collapse.call(this);
56883 var sl = this.view.getSelectedIndexes();
56884 var st = this.store;
56888 Roo.each(sl, function(i) {
56890 nv.push(r.get(this.valueField));
56892 this.setValue(Roo.encode(nv));
56893 if (this.value != this.valueBefore) {
56895 this.fireEvent('change', this, this.value, this.valueBefore);
56896 this.valueBefore = this.value;
56901 setValue : function(v){
56905 var vals = this.getValueArray();
56907 Roo.each(vals, function(k) {
56908 var r = this.findRecord(this.valueField, k);
56910 tv.push(r.data[this.displayField]);
56911 }else if(this.valueNotFoundText !== undefined){
56912 tv.push( this.valueNotFoundText );
56917 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
56918 this.hiddenField.value = v;
56924 * Ext JS Library 1.1.1
56925 * Copyright(c) 2006-2007, Ext JS, LLC.
56927 * Originally Released Under LGPL - original licence link has changed is not relivant.
56930 * <script type="text/javascript">
56934 * @class Roo.form.Signature
56935 * @extends Roo.form.Field
56939 * @param {Object} config Configuration options
56942 Roo.form.Signature = function(config){
56943 Roo.form.Signature.superclass.constructor.call(this, config);
56945 this.addEvents({// not in used??
56948 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
56949 * @param {Roo.form.Signature} combo This combo box
56954 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
56955 * @param {Roo.form.ComboBox} combo This combo box
56956 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
56962 Roo.extend(Roo.form.Signature, Roo.form.Field, {
56964 * @cfg {Object} labels Label to use when rendering a form.
56968 * confirm : "Confirm"
56973 confirm : "Confirm"
56976 * @cfg {Number} width The signature panel width (defaults to 300)
56980 * @cfg {Number} height The signature panel height (defaults to 100)
56984 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
56986 allowBlank : false,
56989 // {Object} signPanel The signature SVG panel element (defaults to {})
56991 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
56992 isMouseDown : false,
56993 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
56994 isConfirmed : false,
56995 // {String} signatureTmp SVG mapping string (defaults to empty string)
56999 defaultAutoCreate : { // modified by initCompnoent..
57005 onRender : function(ct, position){
57007 Roo.form.Signature.superclass.onRender.call(this, ct, position);
57009 this.wrap = this.el.wrap({
57010 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
57013 this.createToolbar(this);
57014 this.signPanel = this.wrap.createChild({
57016 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
57020 this.svgID = Roo.id();
57021 this.svgEl = this.signPanel.createChild({
57022 xmlns : 'http://www.w3.org/2000/svg',
57024 id : this.svgID + "-svg",
57026 height: this.height,
57027 viewBox: '0 0 '+this.width+' '+this.height,
57031 id: this.svgID + "-svg-r",
57033 height: this.height,
57038 id: this.svgID + "-svg-l",
57040 y1: (this.height*0.8), // start set the line in 80% of height
57041 x2: this.width, // end
57042 y2: (this.height*0.8), // end set the line in 80% of height
57044 'stroke-width': "1",
57045 'stroke-dasharray': "3",
57046 'shape-rendering': "crispEdges",
57047 'pointer-events': "none"
57051 id: this.svgID + "-svg-p",
57053 'stroke-width': "3",
57055 'pointer-events': 'none'
57060 this.svgBox = this.svgEl.dom.getScreenCTM();
57062 createSVG : function(){
57063 var svg = this.signPanel;
57064 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
57067 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
57068 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
57069 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
57070 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
57071 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
57072 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
57073 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
57076 isTouchEvent : function(e){
57077 return e.type.match(/^touch/);
57079 getCoords : function (e) {
57080 var pt = this.svgEl.dom.createSVGPoint();
57083 if (this.isTouchEvent(e)) {
57084 pt.x = e.targetTouches[0].clientX;
57085 pt.y = e.targetTouches[0].clientY;
57087 var a = this.svgEl.dom.getScreenCTM();
57088 var b = a.inverse();
57089 var mx = pt.matrixTransform(b);
57090 return mx.x + ',' + mx.y;
57092 //mouse event headler
57093 down : function (e) {
57094 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
57095 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
57097 this.isMouseDown = true;
57099 e.preventDefault();
57101 move : function (e) {
57102 if (this.isMouseDown) {
57103 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
57104 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
57107 e.preventDefault();
57109 up : function (e) {
57110 this.isMouseDown = false;
57111 var sp = this.signatureTmp.split(' ');
57114 if(!sp[sp.length-2].match(/^L/)){
57118 this.signatureTmp = sp.join(" ");
57121 if(this.getValue() != this.signatureTmp){
57122 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57123 this.isConfirmed = false;
57125 e.preventDefault();
57129 * Protected method that will not generally be called directly. It
57130 * is called when the editor creates its toolbar. Override this method if you need to
57131 * add custom toolbar buttons.
57132 * @param {HtmlEditor} editor
57134 createToolbar : function(editor){
57135 function btn(id, toggle, handler){
57136 var xid = fid + '-'+ id ;
57140 cls : 'x-btn-icon x-edit-'+id,
57141 enableToggle:toggle !== false,
57142 scope: editor, // was editor...
57143 handler:handler||editor.relayBtnCmd,
57144 clickEvent:'mousedown',
57145 tooltip: etb.buttonTips[id] || undefined, ///tips ???
57151 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
57155 cls : ' x-signature-btn x-signature-'+id,
57156 scope: editor, // was editor...
57157 handler: this.reset,
57158 clickEvent:'mousedown',
57159 text: this.labels.clear
57166 cls : ' x-signature-btn x-signature-'+id,
57167 scope: editor, // was editor...
57168 handler: this.confirmHandler,
57169 clickEvent:'mousedown',
57170 text: this.labels.confirm
57177 * when user is clicked confirm then show this image.....
57179 * @return {String} Image Data URI
57181 getImageDataURI : function(){
57182 var svg = this.svgEl.dom.parentNode.innerHTML;
57183 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
57188 * @return {Boolean} this.isConfirmed
57190 getConfirmed : function(){
57191 return this.isConfirmed;
57195 * @return {Number} this.width
57197 getWidth : function(){
57202 * @return {Number} this.height
57204 getHeight : function(){
57205 return this.height;
57208 getSignature : function(){
57209 return this.signatureTmp;
57212 reset : function(){
57213 this.signatureTmp = '';
57214 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57215 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
57216 this.isConfirmed = false;
57217 Roo.form.Signature.superclass.reset.call(this);
57219 setSignature : function(s){
57220 this.signatureTmp = s;
57221 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57222 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
57224 this.isConfirmed = false;
57225 Roo.form.Signature.superclass.reset.call(this);
57228 // Roo.log(this.signPanel.dom.contentWindow.up())
57231 setConfirmed : function(){
57235 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
57238 confirmHandler : function(){
57239 if(!this.getSignature()){
57243 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
57244 this.setValue(this.getSignature());
57245 this.isConfirmed = true;
57247 this.fireEvent('confirm', this);
57250 // Subclasses should provide the validation implementation by overriding this
57251 validateValue : function(value){
57252 if(this.allowBlank){
57256 if(this.isConfirmed){
57263 * Ext JS Library 1.1.1
57264 * Copyright(c) 2006-2007, Ext JS, LLC.
57266 * Originally Released Under LGPL - original licence link has changed is not relivant.
57269 * <script type="text/javascript">
57274 * @class Roo.form.ComboBox
57275 * @extends Roo.form.TriggerField
57276 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
57278 * Create a new ComboBox.
57279 * @param {Object} config Configuration options
57281 Roo.form.Select = function(config){
57282 Roo.form.Select.superclass.constructor.call(this, config);
57286 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
57288 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
57291 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
57292 * rendering into an Roo.Editor, defaults to false)
57295 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
57296 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
57299 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
57302 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
57303 * the dropdown list (defaults to undefined, with no header element)
57307 * @cfg {String/Roo.Template} tpl The template to use to render the output
57311 defaultAutoCreate : {tag: "select" },
57313 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
57315 listWidth: undefined,
57317 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
57318 * mode = 'remote' or 'text' if mode = 'local')
57320 displayField: undefined,
57322 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
57323 * mode = 'remote' or 'value' if mode = 'local').
57324 * Note: use of a valueField requires the user make a selection
57325 * in order for a value to be mapped.
57327 valueField: undefined,
57331 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
57332 * field's data value (defaults to the underlying DOM element's name)
57334 hiddenName: undefined,
57336 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
57340 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
57342 selectedClass: 'x-combo-selected',
57344 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
57345 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
57346 * which displays a downward arrow icon).
57348 triggerClass : 'x-form-arrow-trigger',
57350 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
57354 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
57355 * anchor positions (defaults to 'tl-bl')
57357 listAlign: 'tl-bl?',
57359 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
57363 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
57364 * query specified by the allQuery config option (defaults to 'query')
57366 triggerAction: 'query',
57368 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
57369 * (defaults to 4, does not apply if editable = false)
57373 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
57374 * delay (typeAheadDelay) if it matches a known value (defaults to false)
57378 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
57379 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
57383 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
57384 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
57388 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
57389 * when editable = true (defaults to false)
57391 selectOnFocus:false,
57393 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
57395 queryParam: 'query',
57397 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
57398 * when mode = 'remote' (defaults to 'Loading...')
57400 loadingText: 'Loading...',
57402 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
57406 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
57410 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
57411 * traditional select (defaults to true)
57415 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
57419 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
57423 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
57424 * listWidth has a higher value)
57428 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
57429 * allow the user to set arbitrary text into the field (defaults to false)
57431 forceSelection:false,
57433 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
57434 * if typeAhead = true (defaults to 250)
57436 typeAheadDelay : 250,
57438 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
57439 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
57441 valueNotFoundText : undefined,
57444 * @cfg {String} defaultValue The value displayed after loading the store.
57449 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
57451 blockFocus : false,
57454 * @cfg {Boolean} disableClear Disable showing of clear button.
57456 disableClear : false,
57458 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
57460 alwaysQuery : false,
57466 // element that contains real text value.. (when hidden is used..)
57469 onRender : function(ct, position){
57470 Roo.form.Field.prototype.onRender.call(this, ct, position);
57473 this.store.on('beforeload', this.onBeforeLoad, this);
57474 this.store.on('load', this.onLoad, this);
57475 this.store.on('loadexception', this.onLoadException, this);
57476 this.store.load({});
57484 initEvents : function(){
57485 //Roo.form.ComboBox.superclass.initEvents.call(this);
57489 onDestroy : function(){
57492 this.store.un('beforeload', this.onBeforeLoad, this);
57493 this.store.un('load', this.onLoad, this);
57494 this.store.un('loadexception', this.onLoadException, this);
57496 //Roo.form.ComboBox.superclass.onDestroy.call(this);
57500 fireKey : function(e){
57501 if(e.isNavKeyPress() && !this.list.isVisible()){
57502 this.fireEvent("specialkey", this, e);
57507 onResize: function(w, h){
57515 * Allow or prevent the user from directly editing the field text. If false is passed,
57516 * the user will only be able to select from the items defined in the dropdown list. This method
57517 * is the runtime equivalent of setting the 'editable' config option at config time.
57518 * @param {Boolean} value True to allow the user to directly edit the field text
57520 setEditable : function(value){
57525 onBeforeLoad : function(){
57527 Roo.log("Select before load");
57530 this.innerList.update(this.loadingText ?
57531 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
57532 //this.restrictHeight();
57533 this.selectedIndex = -1;
57537 onLoad : function(){
57540 var dom = this.el.dom;
57541 dom.innerHTML = '';
57542 var od = dom.ownerDocument;
57544 if (this.emptyText) {
57545 var op = od.createElement('option');
57546 op.setAttribute('value', '');
57547 op.innerHTML = String.format('{0}', this.emptyText);
57548 dom.appendChild(op);
57550 if(this.store.getCount() > 0){
57552 var vf = this.valueField;
57553 var df = this.displayField;
57554 this.store.data.each(function(r) {
57555 // which colmsn to use... testing - cdoe / title..
57556 var op = od.createElement('option');
57557 op.setAttribute('value', r.data[vf]);
57558 op.innerHTML = String.format('{0}', r.data[df]);
57559 dom.appendChild(op);
57561 if (typeof(this.defaultValue != 'undefined')) {
57562 this.setValue(this.defaultValue);
57567 //this.onEmptyResults();
57572 onLoadException : function()
57574 dom.innerHTML = '';
57576 Roo.log("Select on load exception");
57580 Roo.log(this.store.reader.jsonData);
57581 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57582 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57588 onTypeAhead : function(){
57593 onSelect : function(record, index){
57594 Roo.log('on select?');
57596 if(this.fireEvent('beforeselect', this, record, index) !== false){
57597 this.setFromData(index > -1 ? record.data : false);
57599 this.fireEvent('select', this, record, index);
57604 * Returns the currently selected field value or empty string if no value is set.
57605 * @return {String} value The selected value
57607 getValue : function(){
57608 var dom = this.el.dom;
57609 this.value = dom.options[dom.selectedIndex].value;
57615 * Clears any text/value currently set in the field
57617 clearValue : function(){
57619 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
57624 * Sets the specified value into the field. If the value finds a match, the corresponding record text
57625 * will be displayed in the field. If the value does not match the data value of an existing item,
57626 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
57627 * Otherwise the field will be blank (although the value will still be set).
57628 * @param {String} value The value to match
57630 setValue : function(v){
57631 var d = this.el.dom;
57632 for (var i =0; i < d.options.length;i++) {
57633 if (v == d.options[i].value) {
57634 d.selectedIndex = i;
57642 * @property {Object} the last set data for the element
57647 * Sets the value of the field based on a object which is related to the record format for the store.
57648 * @param {Object} value the value to set as. or false on reset?
57650 setFromData : function(o){
57651 Roo.log('setfrom data?');
57657 reset : function(){
57661 findRecord : function(prop, value){
57666 if(this.store.getCount() > 0){
57667 this.store.each(function(r){
57668 if(r.data[prop] == value){
57678 getName: function()
57680 // returns hidden if it's set..
57681 if (!this.rendered) {return ''};
57682 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
57690 onEmptyResults : function(){
57691 Roo.log('empty results');
57696 * Returns true if the dropdown list is expanded, else false.
57698 isExpanded : function(){
57703 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
57704 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57705 * @param {String} value The data value of the item to select
57706 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57707 * selected item if it is not currently in view (defaults to true)
57708 * @return {Boolean} True if the value matched an item in the list, else false
57710 selectByValue : function(v, scrollIntoView){
57711 Roo.log('select By Value');
57714 if(v !== undefined && v !== null){
57715 var r = this.findRecord(this.valueField || this.displayField, v);
57717 this.select(this.store.indexOf(r), scrollIntoView);
57725 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
57726 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57727 * @param {Number} index The zero-based index of the list item to select
57728 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57729 * selected item if it is not currently in view (defaults to true)
57731 select : function(index, scrollIntoView){
57732 Roo.log('select ');
57735 this.selectedIndex = index;
57736 this.view.select(index);
57737 if(scrollIntoView !== false){
57738 var el = this.view.getNode(index);
57740 this.innerList.scrollChildIntoView(el, false);
57748 validateBlur : function(){
57755 initQuery : function(){
57756 this.doQuery(this.getRawValue());
57760 doForce : function(){
57761 if(this.el.dom.value.length > 0){
57762 this.el.dom.value =
57763 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
57769 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
57770 * query allowing the query action to be canceled if needed.
57771 * @param {String} query The SQL query to execute
57772 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
57773 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
57774 * saved in the current store (defaults to false)
57776 doQuery : function(q, forceAll){
57778 Roo.log('doQuery?');
57779 if(q === undefined || q === null){
57784 forceAll: forceAll,
57788 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
57792 forceAll = qe.forceAll;
57793 if(forceAll === true || (q.length >= this.minChars)){
57794 if(this.lastQuery != q || this.alwaysQuery){
57795 this.lastQuery = q;
57796 if(this.mode == 'local'){
57797 this.selectedIndex = -1;
57799 this.store.clearFilter();
57801 this.store.filter(this.displayField, q);
57805 this.store.baseParams[this.queryParam] = q;
57807 params: this.getParams(q)
57812 this.selectedIndex = -1;
57819 getParams : function(q){
57821 //p[this.queryParam] = q;
57824 p.limit = this.pageSize;
57830 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
57832 collapse : function(){
57837 collapseIf : function(e){
57842 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
57844 expand : function(){
57852 * @cfg {Boolean} grow
57856 * @cfg {Number} growMin
57860 * @cfg {Number} growMax
57868 setWidth : function()
57872 getResizeEl : function(){
57875 });//<script type="text/javasscript">
57879 * @class Roo.DDView
57880 * A DnD enabled version of Roo.View.
57881 * @param {Element/String} container The Element in which to create the View.
57882 * @param {String} tpl The template string used to create the markup for each element of the View
57883 * @param {Object} config The configuration properties. These include all the config options of
57884 * {@link Roo.View} plus some specific to this class.<br>
57886 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
57887 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
57889 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
57890 .x-view-drag-insert-above {
57891 border-top:1px dotted #3366cc;
57893 .x-view-drag-insert-below {
57894 border-bottom:1px dotted #3366cc;
57900 Roo.DDView = function(container, tpl, config) {
57901 Roo.DDView.superclass.constructor.apply(this, arguments);
57902 this.getEl().setStyle("outline", "0px none");
57903 this.getEl().unselectable();
57904 if (this.dragGroup) {
57905 this.setDraggable(this.dragGroup.split(","));
57907 if (this.dropGroup) {
57908 this.setDroppable(this.dropGroup.split(","));
57910 if (this.deletable) {
57911 this.setDeletable();
57913 this.isDirtyFlag = false;
57919 Roo.extend(Roo.DDView, Roo.View, {
57920 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
57921 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
57922 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
57923 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
57927 reset: Roo.emptyFn,
57929 clearInvalid: Roo.form.Field.prototype.clearInvalid,
57931 validate: function() {
57935 destroy: function() {
57936 this.purgeListeners();
57937 this.getEl.removeAllListeners();
57938 this.getEl().remove();
57939 if (this.dragZone) {
57940 if (this.dragZone.destroy) {
57941 this.dragZone.destroy();
57944 if (this.dropZone) {
57945 if (this.dropZone.destroy) {
57946 this.dropZone.destroy();
57951 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
57952 getName: function() {
57956 /** Loads the View from a JSON string representing the Records to put into the Store. */
57957 setValue: function(v) {
57959 throw "DDView.setValue(). DDView must be constructed with a valid Store";
57962 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
57963 this.store.proxy = new Roo.data.MemoryProxy(data);
57967 /** @return {String} a parenthesised list of the ids of the Records in the View. */
57968 getValue: function() {
57970 this.store.each(function(rec) {
57971 result += rec.id + ',';
57973 return result.substr(0, result.length - 1) + ')';
57976 getIds: function() {
57977 var i = 0, result = new Array(this.store.getCount());
57978 this.store.each(function(rec) {
57979 result[i++] = rec.id;
57984 isDirty: function() {
57985 return this.isDirtyFlag;
57989 * Part of the Roo.dd.DropZone interface. If no target node is found, the
57990 * whole Element becomes the target, and this causes the drop gesture to append.
57992 getTargetFromEvent : function(e) {
57993 var target = e.getTarget();
57994 while ((target !== null) && (target.parentNode != this.el.dom)) {
57995 target = target.parentNode;
57998 target = this.el.dom.lastChild || this.el.dom;
58004 * Create the drag data which consists of an object which has the property "ddel" as
58005 * the drag proxy element.
58007 getDragData : function(e) {
58008 var target = this.findItemFromChild(e.getTarget());
58010 this.handleSelection(e);
58011 var selNodes = this.getSelectedNodes();
58014 copy: this.copy || (this.allowCopy && e.ctrlKey),
58018 var selectedIndices = this.getSelectedIndexes();
58019 for (var i = 0; i < selectedIndices.length; i++) {
58020 dragData.records.push(this.store.getAt(selectedIndices[i]));
58022 if (selNodes.length == 1) {
58023 dragData.ddel = target.cloneNode(true); // the div element
58025 var div = document.createElement('div'); // create the multi element drag "ghost"
58026 div.className = 'multi-proxy';
58027 for (var i = 0, len = selNodes.length; i < len; i++) {
58028 div.appendChild(selNodes[i].cloneNode(true));
58030 dragData.ddel = div;
58032 //console.log(dragData)
58033 //console.log(dragData.ddel.innerHTML)
58036 //console.log('nodragData')
58040 /** Specify to which ddGroup items in this DDView may be dragged. */
58041 setDraggable: function(ddGroup) {
58042 if (ddGroup instanceof Array) {
58043 Roo.each(ddGroup, this.setDraggable, this);
58046 if (this.dragZone) {
58047 this.dragZone.addToGroup(ddGroup);
58049 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
58050 containerScroll: true,
58054 // Draggability implies selection. DragZone's mousedown selects the element.
58055 if (!this.multiSelect) { this.singleSelect = true; }
58057 // Wire the DragZone's handlers up to methods in *this*
58058 this.dragZone.getDragData = this.getDragData.createDelegate(this);
58062 /** Specify from which ddGroup this DDView accepts drops. */
58063 setDroppable: function(ddGroup) {
58064 if (ddGroup instanceof Array) {
58065 Roo.each(ddGroup, this.setDroppable, this);
58068 if (this.dropZone) {
58069 this.dropZone.addToGroup(ddGroup);
58071 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
58072 containerScroll: true,
58076 // Wire the DropZone's handlers up to methods in *this*
58077 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
58078 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
58079 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
58080 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
58081 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
58085 /** Decide whether to drop above or below a View node. */
58086 getDropPoint : function(e, n, dd){
58087 if (n == this.el.dom) { return "above"; }
58088 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
58089 var c = t + (b - t) / 2;
58090 var y = Roo.lib.Event.getPageY(e);
58098 onNodeEnter : function(n, dd, e, data){
58102 onNodeOver : function(n, dd, e, data){
58103 var pt = this.getDropPoint(e, n, dd);
58104 // set the insert point style on the target node
58105 var dragElClass = this.dropNotAllowed;
58108 if (pt == "above"){
58109 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
58110 targetElClass = "x-view-drag-insert-above";
58112 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
58113 targetElClass = "x-view-drag-insert-below";
58115 if (this.lastInsertClass != targetElClass){
58116 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
58117 this.lastInsertClass = targetElClass;
58120 return dragElClass;
58123 onNodeOut : function(n, dd, e, data){
58124 this.removeDropIndicators(n);
58127 onNodeDrop : function(n, dd, e, data){
58128 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
58131 var pt = this.getDropPoint(e, n, dd);
58132 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
58133 if (pt == "below") { insertAt++; }
58134 for (var i = 0; i < data.records.length; i++) {
58135 var r = data.records[i];
58136 var dup = this.store.getById(r.id);
58137 if (dup && (dd != this.dragZone)) {
58138 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
58141 this.store.insert(insertAt++, r.copy());
58143 data.source.isDirtyFlag = true;
58145 this.store.insert(insertAt++, r);
58147 this.isDirtyFlag = true;
58150 this.dragZone.cachedTarget = null;
58154 removeDropIndicators : function(n){
58156 Roo.fly(n).removeClass([
58157 "x-view-drag-insert-above",
58158 "x-view-drag-insert-below"]);
58159 this.lastInsertClass = "_noclass";
58164 * Utility method. Add a delete option to the DDView's context menu.
58165 * @param {String} imageUrl The URL of the "delete" icon image.
58167 setDeletable: function(imageUrl) {
58168 if (!this.singleSelect && !this.multiSelect) {
58169 this.singleSelect = true;
58171 var c = this.getContextMenu();
58172 this.contextMenu.on("itemclick", function(item) {
58175 this.remove(this.getSelectedIndexes());
58179 this.contextMenu.add({
58186 /** Return the context menu for this DDView. */
58187 getContextMenu: function() {
58188 if (!this.contextMenu) {
58189 // Create the View's context menu
58190 this.contextMenu = new Roo.menu.Menu({
58191 id: this.id + "-contextmenu"
58193 this.el.on("contextmenu", this.showContextMenu, this);
58195 return this.contextMenu;
58198 disableContextMenu: function() {
58199 if (this.contextMenu) {
58200 this.el.un("contextmenu", this.showContextMenu, this);
58204 showContextMenu: function(e, item) {
58205 item = this.findItemFromChild(e.getTarget());
58208 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
58209 this.contextMenu.showAt(e.getXY());
58214 * Remove {@link Roo.data.Record}s at the specified indices.
58215 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
58217 remove: function(selectedIndices) {
58218 selectedIndices = [].concat(selectedIndices);
58219 for (var i = 0; i < selectedIndices.length; i++) {
58220 var rec = this.store.getAt(selectedIndices[i]);
58221 this.store.remove(rec);
58226 * Double click fires the event, but also, if this is draggable, and there is only one other
58227 * related DropZone, it transfers the selected node.
58229 onDblClick : function(e){
58230 var item = this.findItemFromChild(e.getTarget());
58232 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
58235 if (this.dragGroup) {
58236 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
58237 while (targets.indexOf(this.dropZone) > -1) {
58238 targets.remove(this.dropZone);
58240 if (targets.length == 1) {
58241 this.dragZone.cachedTarget = null;
58242 var el = Roo.get(targets[0].getEl());
58243 var box = el.getBox(true);
58244 targets[0].onNodeDrop(el.dom, {
58246 xy: [box.x, box.y + box.height - 1]
58247 }, null, this.getDragData(e));
58253 handleSelection: function(e) {
58254 this.dragZone.cachedTarget = null;
58255 var item = this.findItemFromChild(e.getTarget());
58257 this.clearSelections(true);
58260 if (item && (this.multiSelect || this.singleSelect)){
58261 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
58262 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
58263 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
58264 this.unselect(item);
58266 this.select(item, this.multiSelect && e.ctrlKey);
58267 this.lastSelection = item;
58272 onItemClick : function(item, index, e){
58273 if(this.fireEvent("beforeclick", this, index, item, e) === false){
58279 unselect : function(nodeInfo, suppressEvent){
58280 var node = this.getNode(nodeInfo);
58281 if(node && this.isSelected(node)){
58282 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
58283 Roo.fly(node).removeClass(this.selectedClass);
58284 this.selections.remove(node);
58285 if(!suppressEvent){
58286 this.fireEvent("selectionchange", this, this.selections);
58294 * Ext JS Library 1.1.1
58295 * Copyright(c) 2006-2007, Ext JS, LLC.
58297 * Originally Released Under LGPL - original licence link has changed is not relivant.
58300 * <script type="text/javascript">
58304 * @class Roo.LayoutManager
58305 * @extends Roo.util.Observable
58306 * Base class for layout managers.
58308 Roo.LayoutManager = function(container, config){
58309 Roo.LayoutManager.superclass.constructor.call(this);
58310 this.el = Roo.get(container);
58311 // ie scrollbar fix
58312 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
58313 document.body.scroll = "no";
58314 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
58315 this.el.position('relative');
58317 this.id = this.el.id;
58318 this.el.addClass("x-layout-container");
58319 /** false to disable window resize monitoring @type Boolean */
58320 this.monitorWindowResize = true;
58325 * Fires when a layout is performed.
58326 * @param {Roo.LayoutManager} this
58330 * @event regionresized
58331 * Fires when the user resizes a region.
58332 * @param {Roo.LayoutRegion} region The resized region
58333 * @param {Number} newSize The new size (width for east/west, height for north/south)
58335 "regionresized" : true,
58337 * @event regioncollapsed
58338 * Fires when a region is collapsed.
58339 * @param {Roo.LayoutRegion} region The collapsed region
58341 "regioncollapsed" : true,
58343 * @event regionexpanded
58344 * Fires when a region is expanded.
58345 * @param {Roo.LayoutRegion} region The expanded region
58347 "regionexpanded" : true
58349 this.updating = false;
58350 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58353 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
58355 * Returns true if this layout is currently being updated
58356 * @return {Boolean}
58358 isUpdating : function(){
58359 return this.updating;
58363 * Suspend the LayoutManager from doing auto-layouts while
58364 * making multiple add or remove calls
58366 beginUpdate : function(){
58367 this.updating = true;
58371 * Restore auto-layouts and optionally disable the manager from performing a layout
58372 * @param {Boolean} noLayout true to disable a layout update
58374 endUpdate : function(noLayout){
58375 this.updating = false;
58381 layout: function(){
58385 onRegionResized : function(region, newSize){
58386 this.fireEvent("regionresized", region, newSize);
58390 onRegionCollapsed : function(region){
58391 this.fireEvent("regioncollapsed", region);
58394 onRegionExpanded : function(region){
58395 this.fireEvent("regionexpanded", region);
58399 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
58400 * performs box-model adjustments.
58401 * @return {Object} The size as an object {width: (the width), height: (the height)}
58403 getViewSize : function(){
58405 if(this.el.dom != document.body){
58406 size = this.el.getSize();
58408 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
58410 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
58411 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
58416 * Returns the Element this layout is bound to.
58417 * @return {Roo.Element}
58419 getEl : function(){
58424 * Returns the specified region.
58425 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
58426 * @return {Roo.LayoutRegion}
58428 getRegion : function(target){
58429 return this.regions[target.toLowerCase()];
58432 onWindowResize : function(){
58433 if(this.monitorWindowResize){
58439 * Ext JS Library 1.1.1
58440 * Copyright(c) 2006-2007, Ext JS, LLC.
58442 * Originally Released Under LGPL - original licence link has changed is not relivant.
58445 * <script type="text/javascript">
58448 * @class Roo.BorderLayout
58449 * @extends Roo.LayoutManager
58450 * @children Roo.ContentPanel
58451 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
58452 * please see: <br><br>
58453 * <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>
58454 * <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>
58457 var layout = new Roo.BorderLayout(document.body, {
58491 preferredTabWidth: 150
58496 var CP = Roo.ContentPanel;
58498 layout.beginUpdate();
58499 layout.add("north", new CP("north", "North"));
58500 layout.add("south", new CP("south", {title: "South", closable: true}));
58501 layout.add("west", new CP("west", {title: "West"}));
58502 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
58503 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
58504 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
58505 layout.getRegion("center").showPanel("center1");
58506 layout.endUpdate();
58509 <b>The container the layout is rendered into can be either the body element or any other element.
58510 If it is not the body element, the container needs to either be an absolute positioned element,
58511 or you will need to add "position:relative" to the css of the container. You will also need to specify
58512 the container size if it is not the body element.</b>
58515 * Create a new BorderLayout
58516 * @param {String/HTMLElement/Element} container The container this layout is bound to
58517 * @param {Object} config Configuration options
58519 Roo.BorderLayout = function(container, config){
58520 config = config || {};
58521 Roo.BorderLayout.superclass.constructor.call(this, container, config);
58522 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
58523 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
58524 var target = this.factory.validRegions[i];
58525 if(config[target]){
58526 this.addRegion(target, config[target]);
58531 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
58534 * @cfg {Roo.LayoutRegion} east
58537 * @cfg {Roo.LayoutRegion} west
58540 * @cfg {Roo.LayoutRegion} north
58543 * @cfg {Roo.LayoutRegion} south
58546 * @cfg {Roo.LayoutRegion} center
58549 * Creates and adds a new region if it doesn't already exist.
58550 * @param {String} target The target region key (north, south, east, west or center).
58551 * @param {Object} config The regions config object
58552 * @return {BorderLayoutRegion} The new region
58554 addRegion : function(target, config){
58555 if(!this.regions[target]){
58556 var r = this.factory.create(target, this, config);
58557 this.bindRegion(target, r);
58559 return this.regions[target];
58563 bindRegion : function(name, r){
58564 this.regions[name] = r;
58565 r.on("visibilitychange", this.layout, this);
58566 r.on("paneladded", this.layout, this);
58567 r.on("panelremoved", this.layout, this);
58568 r.on("invalidated", this.layout, this);
58569 r.on("resized", this.onRegionResized, this);
58570 r.on("collapsed", this.onRegionCollapsed, this);
58571 r.on("expanded", this.onRegionExpanded, this);
58575 * Performs a layout update.
58577 layout : function(){
58578 if(this.updating) {
58581 var size = this.getViewSize();
58582 var w = size.width;
58583 var h = size.height;
58588 //var x = 0, y = 0;
58590 var rs = this.regions;
58591 var north = rs["north"];
58592 var south = rs["south"];
58593 var west = rs["west"];
58594 var east = rs["east"];
58595 var center = rs["center"];
58596 //if(this.hideOnLayout){ // not supported anymore
58597 //c.el.setStyle("display", "none");
58599 if(north && north.isVisible()){
58600 var b = north.getBox();
58601 var m = north.getMargins();
58602 b.width = w - (m.left+m.right);
58605 centerY = b.height + b.y + m.bottom;
58606 centerH -= centerY;
58607 north.updateBox(this.safeBox(b));
58609 if(south && south.isVisible()){
58610 var b = south.getBox();
58611 var m = south.getMargins();
58612 b.width = w - (m.left+m.right);
58614 var totalHeight = (b.height + m.top + m.bottom);
58615 b.y = h - totalHeight + m.top;
58616 centerH -= totalHeight;
58617 south.updateBox(this.safeBox(b));
58619 if(west && west.isVisible()){
58620 var b = west.getBox();
58621 var m = west.getMargins();
58622 b.height = centerH - (m.top+m.bottom);
58624 b.y = centerY + m.top;
58625 var totalWidth = (b.width + m.left + m.right);
58626 centerX += totalWidth;
58627 centerW -= totalWidth;
58628 west.updateBox(this.safeBox(b));
58630 if(east && east.isVisible()){
58631 var b = east.getBox();
58632 var m = east.getMargins();
58633 b.height = centerH - (m.top+m.bottom);
58634 var totalWidth = (b.width + m.left + m.right);
58635 b.x = w - totalWidth + m.left;
58636 b.y = centerY + m.top;
58637 centerW -= totalWidth;
58638 east.updateBox(this.safeBox(b));
58641 var m = center.getMargins();
58643 x: centerX + m.left,
58644 y: centerY + m.top,
58645 width: centerW - (m.left+m.right),
58646 height: centerH - (m.top+m.bottom)
58648 //if(this.hideOnLayout){
58649 //center.el.setStyle("display", "block");
58651 center.updateBox(this.safeBox(centerBox));
58654 this.fireEvent("layout", this);
58658 safeBox : function(box){
58659 box.width = Math.max(0, box.width);
58660 box.height = Math.max(0, box.height);
58665 * Adds a ContentPanel (or subclass) to this layout.
58666 * @param {String} target The target region key (north, south, east, west or center).
58667 * @param {Roo.ContentPanel} panel The panel to add
58668 * @return {Roo.ContentPanel} The added panel
58670 add : function(target, panel){
58672 target = target.toLowerCase();
58673 return this.regions[target].add(panel);
58677 * Remove a ContentPanel (or subclass) to this layout.
58678 * @param {String} target The target region key (north, south, east, west or center).
58679 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
58680 * @return {Roo.ContentPanel} The removed panel
58682 remove : function(target, panel){
58683 target = target.toLowerCase();
58684 return this.regions[target].remove(panel);
58688 * Searches all regions for a panel with the specified id
58689 * @param {String} panelId
58690 * @return {Roo.ContentPanel} The panel or null if it wasn't found
58692 findPanel : function(panelId){
58693 var rs = this.regions;
58694 for(var target in rs){
58695 if(typeof rs[target] != "function"){
58696 var p = rs[target].getPanel(panelId);
58706 * Searches all regions for a panel with the specified id and activates (shows) it.
58707 * @param {String/ContentPanel} panelId The panels id or the panel itself
58708 * @return {Roo.ContentPanel} The shown panel or null
58710 showPanel : function(panelId) {
58711 var rs = this.regions;
58712 for(var target in rs){
58713 var r = rs[target];
58714 if(typeof r != "function"){
58715 if(r.hasPanel(panelId)){
58716 return r.showPanel(panelId);
58724 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
58725 * @param {Roo.state.Provider} provider (optional) An alternate state provider
58727 restoreState : function(provider){
58729 provider = Roo.state.Manager;
58731 var sm = new Roo.LayoutStateManager();
58732 sm.init(this, provider);
58736 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
58737 * object should contain properties for each region to add ContentPanels to, and each property's value should be
58738 * a valid ContentPanel config object. Example:
58740 // Create the main layout
58741 var layout = new Roo.BorderLayout('main-ct', {
58752 // Create and add multiple ContentPanels at once via configs
58755 id: 'source-files',
58757 title:'Ext Source Files',
58770 * @param {Object} regions An object containing ContentPanel configs by region name
58772 batchAdd : function(regions){
58773 this.beginUpdate();
58774 for(var rname in regions){
58775 var lr = this.regions[rname];
58777 this.addTypedPanels(lr, regions[rname]);
58784 addTypedPanels : function(lr, ps){
58785 if(typeof ps == 'string'){
58786 lr.add(new Roo.ContentPanel(ps));
58788 else if(ps instanceof Array){
58789 for(var i =0, len = ps.length; i < len; i++){
58790 this.addTypedPanels(lr, ps[i]);
58793 else if(!ps.events){ // raw config?
58795 delete ps.el; // prevent conflict
58796 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
58798 else { // panel object assumed!
58803 * Adds a xtype elements to the layout.
58807 xtype : 'ContentPanel',
58814 xtype : 'NestedLayoutPanel',
58820 items : [ ... list of content panels or nested layout panels.. ]
58824 * @param {Object} cfg Xtype definition of item to add.
58826 addxtype : function(cfg)
58828 // basically accepts a pannel...
58829 // can accept a layout region..!?!?
58830 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
58832 if (!cfg.xtype.match(/Panel$/)) {
58837 if (typeof(cfg.region) == 'undefined') {
58838 Roo.log("Failed to add Panel, region was not set");
58842 var region = cfg.region;
58848 xitems = cfg.items;
58855 case 'ContentPanel': // ContentPanel (el, cfg)
58856 case 'ScrollPanel': // ContentPanel (el, cfg)
58858 if(cfg.autoCreate) {
58859 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58861 var el = this.el.createChild();
58862 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
58865 this.add(region, ret);
58869 case 'TreePanel': // our new panel!
58870 cfg.el = this.el.createChild();
58871 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58872 this.add(region, ret);
58875 case 'NestedLayoutPanel':
58876 // create a new Layout (which is a Border Layout...
58877 var el = this.el.createChild();
58878 var clayout = cfg.layout;
58880 clayout.items = clayout.items || [];
58881 // replace this exitems with the clayout ones..
58882 xitems = clayout.items;
58885 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
58886 cfg.background = false;
58888 var layout = new Roo.BorderLayout(el, clayout);
58890 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
58891 //console.log('adding nested layout panel ' + cfg.toSource());
58892 this.add(region, ret);
58893 nb = {}; /// find first...
58898 // needs grid and region
58900 //var el = this.getRegion(region).el.createChild();
58901 var el = this.el.createChild();
58902 // create the grid first...
58904 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
58906 if (region == 'center' && this.active ) {
58907 cfg.background = false;
58909 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
58911 this.add(region, ret);
58912 if (cfg.background) {
58913 ret.on('activate', function(gp) {
58914 if (!gp.grid.rendered) {
58929 if (typeof(Roo[cfg.xtype]) != 'undefined') {
58931 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58932 this.add(region, ret);
58935 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
58939 // GridPanel (grid, cfg)
58942 this.beginUpdate();
58946 Roo.each(xitems, function(i) {
58947 region = nb && i.region ? i.region : false;
58949 var add = ret.addxtype(i);
58952 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
58953 if (!i.background) {
58954 abn[region] = nb[region] ;
58961 // make the last non-background panel active..
58962 //if (nb) { Roo.log(abn); }
58965 for(var r in abn) {
58966 region = this.getRegion(r);
58968 // tried using nb[r], but it does not work..
58970 region.showPanel(abn[r]);
58981 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
58982 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
58983 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
58984 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
58987 var CP = Roo.ContentPanel;
58989 var layout = Roo.BorderLayout.create({
58993 panels: [new CP("north", "North")]
59002 panels: [new CP("west", {title: "West"})]
59011 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
59020 panels: [new CP("south", {title: "South", closable: true})]
59027 preferredTabWidth: 150,
59029 new CP("center1", {title: "Close Me", closable: true}),
59030 new CP("center2", {title: "Center Panel", closable: false})
59035 layout.getRegion("center").showPanel("center1");
59040 Roo.BorderLayout.create = function(config, targetEl){
59041 var layout = new Roo.BorderLayout(targetEl || document.body, config);
59042 layout.beginUpdate();
59043 var regions = Roo.BorderLayout.RegionFactory.validRegions;
59044 for(var j = 0, jlen = regions.length; j < jlen; j++){
59045 var lr = regions[j];
59046 if(layout.regions[lr] && config[lr].panels){
59047 var r = layout.regions[lr];
59048 var ps = config[lr].panels;
59049 layout.addTypedPanels(r, ps);
59052 layout.endUpdate();
59057 Roo.BorderLayout.RegionFactory = {
59059 validRegions : ["north","south","east","west","center"],
59062 create : function(target, mgr, config){
59063 target = target.toLowerCase();
59064 if(config.lightweight || config.basic){
59065 return new Roo.BasicLayoutRegion(mgr, config, target);
59069 return new Roo.NorthLayoutRegion(mgr, config);
59071 return new Roo.SouthLayoutRegion(mgr, config);
59073 return new Roo.EastLayoutRegion(mgr, config);
59075 return new Roo.WestLayoutRegion(mgr, config);
59077 return new Roo.CenterLayoutRegion(mgr, config);
59079 throw 'Layout region "'+target+'" not supported.';
59083 * Ext JS Library 1.1.1
59084 * Copyright(c) 2006-2007, Ext JS, LLC.
59086 * Originally Released Under LGPL - original licence link has changed is not relivant.
59089 * <script type="text/javascript">
59093 * @class Roo.BasicLayoutRegion
59094 * @extends Roo.util.Observable
59095 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
59096 * and does not have a titlebar, tabs or any other features. All it does is size and position
59097 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
59099 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
59101 this.position = pos;
59104 * @scope Roo.BasicLayoutRegion
59108 * @event beforeremove
59109 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
59110 * @param {Roo.LayoutRegion} this
59111 * @param {Roo.ContentPanel} panel The panel
59112 * @param {Object} e The cancel event object
59114 "beforeremove" : true,
59116 * @event invalidated
59117 * Fires when the layout for this region is changed.
59118 * @param {Roo.LayoutRegion} this
59120 "invalidated" : true,
59122 * @event visibilitychange
59123 * Fires when this region is shown or hidden
59124 * @param {Roo.LayoutRegion} this
59125 * @param {Boolean} visibility true or false
59127 "visibilitychange" : true,
59129 * @event paneladded
59130 * Fires when a panel is added.
59131 * @param {Roo.LayoutRegion} this
59132 * @param {Roo.ContentPanel} panel The panel
59134 "paneladded" : true,
59136 * @event panelremoved
59137 * Fires when a panel is removed.
59138 * @param {Roo.LayoutRegion} this
59139 * @param {Roo.ContentPanel} panel The panel
59141 "panelremoved" : true,
59143 * @event beforecollapse
59144 * Fires when this region before collapse.
59145 * @param {Roo.LayoutRegion} this
59147 "beforecollapse" : true,
59150 * Fires when this region is collapsed.
59151 * @param {Roo.LayoutRegion} this
59153 "collapsed" : true,
59156 * Fires when this region is expanded.
59157 * @param {Roo.LayoutRegion} this
59162 * Fires when this region is slid into view.
59163 * @param {Roo.LayoutRegion} this
59165 "slideshow" : true,
59168 * Fires when this region slides out of view.
59169 * @param {Roo.LayoutRegion} this
59171 "slidehide" : true,
59173 * @event panelactivated
59174 * Fires when a panel is activated.
59175 * @param {Roo.LayoutRegion} this
59176 * @param {Roo.ContentPanel} panel The activated panel
59178 "panelactivated" : true,
59181 * Fires when the user resizes this region.
59182 * @param {Roo.LayoutRegion} this
59183 * @param {Number} newSize The new size (width for east/west, height for north/south)
59187 /** A collection of panels in this region. @type Roo.util.MixedCollection */
59188 this.panels = new Roo.util.MixedCollection();
59189 this.panels.getKey = this.getPanelId.createDelegate(this);
59191 this.activePanel = null;
59192 // ensure listeners are added...
59194 if (config.listeners || config.events) {
59195 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
59196 listeners : config.listeners || {},
59197 events : config.events || {}
59201 if(skipConfig !== true){
59202 this.applyConfig(config);
59206 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
59207 getPanelId : function(p){
59211 applyConfig : function(config){
59212 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59213 this.config = config;
59218 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
59219 * the width, for horizontal (north, south) the height.
59220 * @param {Number} newSize The new width or height
59222 resizeTo : function(newSize){
59223 var el = this.el ? this.el :
59224 (this.activePanel ? this.activePanel.getEl() : null);
59226 switch(this.position){
59229 el.setWidth(newSize);
59230 this.fireEvent("resized", this, newSize);
59234 el.setHeight(newSize);
59235 this.fireEvent("resized", this, newSize);
59241 getBox : function(){
59242 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
59245 getMargins : function(){
59246 return this.margins;
59249 updateBox : function(box){
59251 var el = this.activePanel.getEl();
59252 el.dom.style.left = box.x + "px";
59253 el.dom.style.top = box.y + "px";
59254 this.activePanel.setSize(box.width, box.height);
59258 * Returns the container element for this region.
59259 * @return {Roo.Element}
59261 getEl : function(){
59262 return this.activePanel;
59266 * Returns true if this region is currently visible.
59267 * @return {Boolean}
59269 isVisible : function(){
59270 return this.activePanel ? true : false;
59273 setActivePanel : function(panel){
59274 panel = this.getPanel(panel);
59275 if(this.activePanel && this.activePanel != panel){
59276 this.activePanel.setActiveState(false);
59277 this.activePanel.getEl().setLeftTop(-10000,-10000);
59279 this.activePanel = panel;
59280 panel.setActiveState(true);
59282 panel.setSize(this.box.width, this.box.height);
59284 this.fireEvent("panelactivated", this, panel);
59285 this.fireEvent("invalidated");
59289 * Show the specified panel.
59290 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
59291 * @return {Roo.ContentPanel} The shown panel or null
59293 showPanel : function(panel){
59294 if(panel = this.getPanel(panel)){
59295 this.setActivePanel(panel);
59301 * Get the active panel for this region.
59302 * @return {Roo.ContentPanel} The active panel or null
59304 getActivePanel : function(){
59305 return this.activePanel;
59309 * Add the passed ContentPanel(s)
59310 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59311 * @return {Roo.ContentPanel} The panel added (if only one was added)
59313 add : function(panel){
59314 if(arguments.length > 1){
59315 for(var i = 0, len = arguments.length; i < len; i++) {
59316 this.add(arguments[i]);
59320 if(this.hasPanel(panel)){
59321 this.showPanel(panel);
59324 var el = panel.getEl();
59325 if(el.dom.parentNode != this.mgr.el.dom){
59326 this.mgr.el.dom.appendChild(el.dom);
59328 if(panel.setRegion){
59329 panel.setRegion(this);
59331 this.panels.add(panel);
59332 el.setStyle("position", "absolute");
59333 if(!panel.background){
59334 this.setActivePanel(panel);
59335 if(this.config.initialSize && this.panels.getCount()==1){
59336 this.resizeTo(this.config.initialSize);
59339 this.fireEvent("paneladded", this, panel);
59344 * Returns true if the panel is in this region.
59345 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59346 * @return {Boolean}
59348 hasPanel : function(panel){
59349 if(typeof panel == "object"){ // must be panel obj
59350 panel = panel.getId();
59352 return this.getPanel(panel) ? true : false;
59356 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59357 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59358 * @param {Boolean} preservePanel Overrides the config preservePanel option
59359 * @return {Roo.ContentPanel} The panel that was removed
59361 remove : function(panel, preservePanel){
59362 panel = this.getPanel(panel);
59367 this.fireEvent("beforeremove", this, panel, e);
59368 if(e.cancel === true){
59371 var panelId = panel.getId();
59372 this.panels.removeKey(panelId);
59377 * Returns the panel specified or null if it's not in this region.
59378 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59379 * @return {Roo.ContentPanel}
59381 getPanel : function(id){
59382 if(typeof id == "object"){ // must be panel obj
59385 return this.panels.get(id);
59389 * Returns this regions position (north/south/east/west/center).
59392 getPosition: function(){
59393 return this.position;
59397 * Ext JS Library 1.1.1
59398 * Copyright(c) 2006-2007, Ext JS, LLC.
59400 * Originally Released Under LGPL - original licence link has changed is not relivant.
59403 * <script type="text/javascript">
59407 * @class Roo.LayoutRegion
59408 * @extends Roo.BasicLayoutRegion
59409 * This class represents a region in a layout manager.
59410 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
59411 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
59412 * @cfg {Boolean} floatable False to disable floating (defaults to true)
59413 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
59414 * @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})
59415 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
59416 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
59417 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
59418 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
59419 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
59420 * @cfg {String} title The title for the region (overrides panel titles)
59421 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
59422 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
59423 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
59424 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
59425 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
59426 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
59427 * the space available, similar to FireFox 1.5 tabs (defaults to false)
59428 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
59429 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
59430 * @cfg {Boolean} showPin True to show a pin button
59431 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
59432 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
59433 * @cfg {Boolean} disableTabTips True to disable tab tooltips
59434 * @cfg {Number} width For East/West panels
59435 * @cfg {Number} height For North/South panels
59436 * @cfg {Boolean} split To show the splitter
59437 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
59439 Roo.LayoutRegion = function(mgr, config, pos){
59440 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
59441 var dh = Roo.DomHelper;
59442 /** This region's container element
59443 * @type Roo.Element */
59444 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
59445 /** This region's title element
59446 * @type Roo.Element */
59448 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
59449 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
59450 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
59452 this.titleEl.enableDisplayMode();
59453 /** This region's title text element
59454 * @type HTMLElement */
59455 this.titleTextEl = this.titleEl.dom.firstChild;
59456 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
59457 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
59458 this.closeBtn.enableDisplayMode();
59459 this.closeBtn.on("click", this.closeClicked, this);
59460 this.closeBtn.hide();
59462 this.createBody(config);
59463 this.visible = true;
59464 this.collapsed = false;
59466 if(config.hideWhenEmpty){
59468 this.on("paneladded", this.validateVisibility, this);
59469 this.on("panelremoved", this.validateVisibility, this);
59471 this.applyConfig(config);
59474 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
59476 createBody : function(){
59477 /** This region's body element
59478 * @type Roo.Element */
59479 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
59482 applyConfig : function(c){
59483 if(c.collapsible && this.position != "center" && !this.collapsedEl){
59484 var dh = Roo.DomHelper;
59485 if(c.titlebar !== false){
59486 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
59487 this.collapseBtn.on("click", this.collapse, this);
59488 this.collapseBtn.enableDisplayMode();
59490 if(c.showPin === true || this.showPin){
59491 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
59492 this.stickBtn.enableDisplayMode();
59493 this.stickBtn.on("click", this.expand, this);
59494 this.stickBtn.hide();
59497 /** This region's collapsed element
59498 * @type Roo.Element */
59499 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
59500 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
59502 if(c.floatable !== false){
59503 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
59504 this.collapsedEl.on("click", this.collapseClick, this);
59507 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
59508 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
59509 id: "message", unselectable: "on", style:{"float":"left"}});
59510 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
59512 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
59513 this.expandBtn.on("click", this.expand, this);
59515 if(this.collapseBtn){
59516 this.collapseBtn.setVisible(c.collapsible == true);
59518 this.cmargins = c.cmargins || this.cmargins ||
59519 (this.position == "west" || this.position == "east" ?
59520 {top: 0, left: 2, right:2, bottom: 0} :
59521 {top: 2, left: 0, right:0, bottom: 2});
59522 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59523 this.bottomTabs = c.tabPosition != "top";
59524 this.autoScroll = c.autoScroll || false;
59525 if(this.autoScroll){
59526 this.bodyEl.setStyle("overflow", "auto");
59528 this.bodyEl.setStyle("overflow", "hidden");
59530 //if(c.titlebar !== false){
59531 if((!c.titlebar && !c.title) || c.titlebar === false){
59532 this.titleEl.hide();
59534 this.titleEl.show();
59536 this.titleTextEl.innerHTML = c.title;
59540 this.duration = c.duration || .30;
59541 this.slideDuration = c.slideDuration || .45;
59544 this.collapse(true);
59551 * Returns true if this region is currently visible.
59552 * @return {Boolean}
59554 isVisible : function(){
59555 return this.visible;
59559 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
59560 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
59562 setCollapsedTitle : function(title){
59563 title = title || " ";
59564 if(this.collapsedTitleTextEl){
59565 this.collapsedTitleTextEl.innerHTML = title;
59569 getBox : function(){
59571 if(!this.collapsed){
59572 b = this.el.getBox(false, true);
59574 b = this.collapsedEl.getBox(false, true);
59579 getMargins : function(){
59580 return this.collapsed ? this.cmargins : this.margins;
59583 highlight : function(){
59584 this.el.addClass("x-layout-panel-dragover");
59587 unhighlight : function(){
59588 this.el.removeClass("x-layout-panel-dragover");
59591 updateBox : function(box){
59593 if(!this.collapsed){
59594 this.el.dom.style.left = box.x + "px";
59595 this.el.dom.style.top = box.y + "px";
59596 this.updateBody(box.width, box.height);
59598 this.collapsedEl.dom.style.left = box.x + "px";
59599 this.collapsedEl.dom.style.top = box.y + "px";
59600 this.collapsedEl.setSize(box.width, box.height);
59603 this.tabs.autoSizeTabs();
59607 updateBody : function(w, h){
59609 this.el.setWidth(w);
59610 w -= this.el.getBorderWidth("rl");
59611 if(this.config.adjustments){
59612 w += this.config.adjustments[0];
59616 this.el.setHeight(h);
59617 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
59618 h -= this.el.getBorderWidth("tb");
59619 if(this.config.adjustments){
59620 h += this.config.adjustments[1];
59622 this.bodyEl.setHeight(h);
59624 h = this.tabs.syncHeight(h);
59627 if(this.panelSize){
59628 w = w !== null ? w : this.panelSize.width;
59629 h = h !== null ? h : this.panelSize.height;
59631 if(this.activePanel){
59632 var el = this.activePanel.getEl();
59633 w = w !== null ? w : el.getWidth();
59634 h = h !== null ? h : el.getHeight();
59635 this.panelSize = {width: w, height: h};
59636 this.activePanel.setSize(w, h);
59638 if(Roo.isIE && this.tabs){
59639 this.tabs.el.repaint();
59644 * Returns the container element for this region.
59645 * @return {Roo.Element}
59647 getEl : function(){
59652 * Hides this region.
59655 if(!this.collapsed){
59656 this.el.dom.style.left = "-2000px";
59659 this.collapsedEl.dom.style.left = "-2000px";
59660 this.collapsedEl.hide();
59662 this.visible = false;
59663 this.fireEvent("visibilitychange", this, false);
59667 * Shows this region if it was previously hidden.
59670 if(!this.collapsed){
59673 this.collapsedEl.show();
59675 this.visible = true;
59676 this.fireEvent("visibilitychange", this, true);
59679 closeClicked : function(){
59680 if(this.activePanel){
59681 this.remove(this.activePanel);
59685 collapseClick : function(e){
59687 e.stopPropagation();
59690 e.stopPropagation();
59696 * Collapses this region.
59697 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
59699 collapse : function(skipAnim, skipCheck){
59700 if(this.collapsed) {
59704 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
59706 this.collapsed = true;
59708 this.split.el.hide();
59710 if(this.config.animate && skipAnim !== true){
59711 this.fireEvent("invalidated", this);
59712 this.animateCollapse();
59714 this.el.setLocation(-20000,-20000);
59716 this.collapsedEl.show();
59717 this.fireEvent("collapsed", this);
59718 this.fireEvent("invalidated", this);
59724 animateCollapse : function(){
59729 * Expands this region if it was previously collapsed.
59730 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
59731 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
59733 expand : function(e, skipAnim){
59735 e.stopPropagation();
59737 if(!this.collapsed || this.el.hasActiveFx()) {
59741 this.afterSlideIn();
59744 this.collapsed = false;
59745 if(this.config.animate && skipAnim !== true){
59746 this.animateExpand();
59750 this.split.el.show();
59752 this.collapsedEl.setLocation(-2000,-2000);
59753 this.collapsedEl.hide();
59754 this.fireEvent("invalidated", this);
59755 this.fireEvent("expanded", this);
59759 animateExpand : function(){
59763 initTabs : function()
59765 this.bodyEl.setStyle("overflow", "hidden");
59766 var ts = new Roo.TabPanel(
59769 tabPosition: this.bottomTabs ? 'bottom' : 'top',
59770 disableTooltips: this.config.disableTabTips,
59771 toolbar : this.config.toolbar
59774 if(this.config.hideTabs){
59775 ts.stripWrap.setDisplayed(false);
59778 ts.resizeTabs = this.config.resizeTabs === true;
59779 ts.minTabWidth = this.config.minTabWidth || 40;
59780 ts.maxTabWidth = this.config.maxTabWidth || 250;
59781 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
59782 ts.monitorResize = false;
59783 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59784 ts.bodyEl.addClass('x-layout-tabs-body');
59785 this.panels.each(this.initPanelAsTab, this);
59788 initPanelAsTab : function(panel){
59789 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
59790 this.config.closeOnTab && panel.isClosable());
59791 if(panel.tabTip !== undefined){
59792 ti.setTooltip(panel.tabTip);
59794 ti.on("activate", function(){
59795 this.setActivePanel(panel);
59797 if(this.config.closeOnTab){
59798 ti.on("beforeclose", function(t, e){
59800 this.remove(panel);
59806 updatePanelTitle : function(panel, title){
59807 if(this.activePanel == panel){
59808 this.updateTitle(title);
59811 var ti = this.tabs.getTab(panel.getEl().id);
59813 if(panel.tabTip !== undefined){
59814 ti.setTooltip(panel.tabTip);
59819 updateTitle : function(title){
59820 if(this.titleTextEl && !this.config.title){
59821 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
59825 setActivePanel : function(panel){
59826 panel = this.getPanel(panel);
59827 if(this.activePanel && this.activePanel != panel){
59828 this.activePanel.setActiveState(false);
59830 this.activePanel = panel;
59831 panel.setActiveState(true);
59832 if(this.panelSize){
59833 panel.setSize(this.panelSize.width, this.panelSize.height);
59836 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
59838 this.updateTitle(panel.getTitle());
59840 this.fireEvent("invalidated", this);
59842 this.fireEvent("panelactivated", this, panel);
59846 * Shows the specified panel.
59847 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
59848 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
59850 showPanel : function(panel)
59852 panel = this.getPanel(panel);
59855 var tab = this.tabs.getTab(panel.getEl().id);
59856 if(tab.isHidden()){
59857 this.tabs.unhideTab(tab.id);
59861 this.setActivePanel(panel);
59868 * Get the active panel for this region.
59869 * @return {Roo.ContentPanel} The active panel or null
59871 getActivePanel : function(){
59872 return this.activePanel;
59875 validateVisibility : function(){
59876 if(this.panels.getCount() < 1){
59877 this.updateTitle(" ");
59878 this.closeBtn.hide();
59881 if(!this.isVisible()){
59888 * Adds the passed ContentPanel(s) to this region.
59889 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59890 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
59892 add : function(panel){
59893 if(arguments.length > 1){
59894 for(var i = 0, len = arguments.length; i < len; i++) {
59895 this.add(arguments[i]);
59899 if(this.hasPanel(panel)){
59900 this.showPanel(panel);
59903 panel.setRegion(this);
59904 this.panels.add(panel);
59905 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
59906 this.bodyEl.dom.appendChild(panel.getEl().dom);
59907 if(panel.background !== true){
59908 this.setActivePanel(panel);
59910 this.fireEvent("paneladded", this, panel);
59916 this.initPanelAsTab(panel);
59918 if(panel.background !== true){
59919 this.tabs.activate(panel.getEl().id);
59921 this.fireEvent("paneladded", this, panel);
59926 * Hides the tab for the specified panel.
59927 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59929 hidePanel : function(panel){
59930 if(this.tabs && (panel = this.getPanel(panel))){
59931 this.tabs.hideTab(panel.getEl().id);
59936 * Unhides the tab for a previously hidden panel.
59937 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59939 unhidePanel : function(panel){
59940 if(this.tabs && (panel = this.getPanel(panel))){
59941 this.tabs.unhideTab(panel.getEl().id);
59945 clearPanels : function(){
59946 while(this.panels.getCount() > 0){
59947 this.remove(this.panels.first());
59952 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59953 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59954 * @param {Boolean} preservePanel Overrides the config preservePanel option
59955 * @return {Roo.ContentPanel} The panel that was removed
59957 remove : function(panel, preservePanel){
59958 panel = this.getPanel(panel);
59963 this.fireEvent("beforeremove", this, panel, e);
59964 if(e.cancel === true){
59967 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
59968 var panelId = panel.getId();
59969 this.panels.removeKey(panelId);
59971 document.body.appendChild(panel.getEl().dom);
59974 this.tabs.removeTab(panel.getEl().id);
59975 }else if (!preservePanel){
59976 this.bodyEl.dom.removeChild(panel.getEl().dom);
59978 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
59979 var p = this.panels.first();
59980 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
59981 tempEl.appendChild(p.getEl().dom);
59982 this.bodyEl.update("");
59983 this.bodyEl.dom.appendChild(p.getEl().dom);
59985 this.updateTitle(p.getTitle());
59987 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59988 this.setActivePanel(p);
59990 panel.setRegion(null);
59991 if(this.activePanel == panel){
59992 this.activePanel = null;
59994 if(this.config.autoDestroy !== false && preservePanel !== true){
59995 try{panel.destroy();}catch(e){}
59997 this.fireEvent("panelremoved", this, panel);
60002 * Returns the TabPanel component used by this region
60003 * @return {Roo.TabPanel}
60005 getTabs : function(){
60009 createTool : function(parentEl, className){
60010 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
60011 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
60012 btn.addClassOnOver("x-layout-tools-button-over");
60017 * Ext JS Library 1.1.1
60018 * Copyright(c) 2006-2007, Ext JS, LLC.
60020 * Originally Released Under LGPL - original licence link has changed is not relivant.
60023 * <script type="text/javascript">
60029 * @class Roo.SplitLayoutRegion
60030 * @extends Roo.LayoutRegion
60031 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
60033 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
60034 this.cursor = cursor;
60035 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
60038 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
60039 splitTip : "Drag to resize.",
60040 collapsibleSplitTip : "Drag to resize. Double click to hide.",
60041 useSplitTips : false,
60043 applyConfig : function(config){
60044 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
60047 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
60048 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
60049 /** The SplitBar for this region
60050 * @type Roo.SplitBar */
60051 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
60052 this.split.on("moved", this.onSplitMove, this);
60053 this.split.useShim = config.useShim === true;
60054 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
60055 if(this.useSplitTips){
60056 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
60058 if(config.collapsible){
60059 this.split.el.on("dblclick", this.collapse, this);
60062 if(typeof config.minSize != "undefined"){
60063 this.split.minSize = config.minSize;
60065 if(typeof config.maxSize != "undefined"){
60066 this.split.maxSize = config.maxSize;
60068 if(config.hideWhenEmpty || config.hidden || config.collapsed){
60069 this.hideSplitter();
60074 getHMaxSize : function(){
60075 var cmax = this.config.maxSize || 10000;
60076 var center = this.mgr.getRegion("center");
60077 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
60080 getVMaxSize : function(){
60081 var cmax = this.config.maxSize || 10000;
60082 var center = this.mgr.getRegion("center");
60083 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
60086 onSplitMove : function(split, newSize){
60087 this.fireEvent("resized", this, newSize);
60091 * Returns the {@link Roo.SplitBar} for this region.
60092 * @return {Roo.SplitBar}
60094 getSplitBar : function(){
60099 this.hideSplitter();
60100 Roo.SplitLayoutRegion.superclass.hide.call(this);
60103 hideSplitter : function(){
60105 this.split.el.setLocation(-2000,-2000);
60106 this.split.el.hide();
60112 this.split.el.show();
60114 Roo.SplitLayoutRegion.superclass.show.call(this);
60117 beforeSlide: function(){
60118 if(Roo.isGecko){// firefox overflow auto bug workaround
60119 this.bodyEl.clip();
60121 this.tabs.bodyEl.clip();
60123 if(this.activePanel){
60124 this.activePanel.getEl().clip();
60126 if(this.activePanel.beforeSlide){
60127 this.activePanel.beforeSlide();
60133 afterSlide : function(){
60134 if(Roo.isGecko){// firefox overflow auto bug workaround
60135 this.bodyEl.unclip();
60137 this.tabs.bodyEl.unclip();
60139 if(this.activePanel){
60140 this.activePanel.getEl().unclip();
60141 if(this.activePanel.afterSlide){
60142 this.activePanel.afterSlide();
60148 initAutoHide : function(){
60149 if(this.autoHide !== false){
60150 if(!this.autoHideHd){
60151 var st = new Roo.util.DelayedTask(this.slideIn, this);
60152 this.autoHideHd = {
60153 "mouseout": function(e){
60154 if(!e.within(this.el, true)){
60158 "mouseover" : function(e){
60164 this.el.on(this.autoHideHd);
60168 clearAutoHide : function(){
60169 if(this.autoHide !== false){
60170 this.el.un("mouseout", this.autoHideHd.mouseout);
60171 this.el.un("mouseover", this.autoHideHd.mouseover);
60175 clearMonitor : function(){
60176 Roo.get(document).un("click", this.slideInIf, this);
60179 // these names are backwards but not changed for compat
60180 slideOut : function(){
60181 if(this.isSlid || this.el.hasActiveFx()){
60184 this.isSlid = true;
60185 if(this.collapseBtn){
60186 this.collapseBtn.hide();
60188 this.closeBtnState = this.closeBtn.getStyle('display');
60189 this.closeBtn.hide();
60191 this.stickBtn.show();
60194 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
60195 this.beforeSlide();
60196 this.el.setStyle("z-index", 10001);
60197 this.el.slideIn(this.getSlideAnchor(), {
60198 callback: function(){
60200 this.initAutoHide();
60201 Roo.get(document).on("click", this.slideInIf, this);
60202 this.fireEvent("slideshow", this);
60209 afterSlideIn : function(){
60210 this.clearAutoHide();
60211 this.isSlid = false;
60212 this.clearMonitor();
60213 this.el.setStyle("z-index", "");
60214 if(this.collapseBtn){
60215 this.collapseBtn.show();
60217 this.closeBtn.setStyle('display', this.closeBtnState);
60219 this.stickBtn.hide();
60221 this.fireEvent("slidehide", this);
60224 slideIn : function(cb){
60225 if(!this.isSlid || this.el.hasActiveFx()){
60229 this.isSlid = false;
60230 this.beforeSlide();
60231 this.el.slideOut(this.getSlideAnchor(), {
60232 callback: function(){
60233 this.el.setLeftTop(-10000, -10000);
60235 this.afterSlideIn();
60243 slideInIf : function(e){
60244 if(!e.within(this.el)){
60249 animateCollapse : function(){
60250 this.beforeSlide();
60251 this.el.setStyle("z-index", 20000);
60252 var anchor = this.getSlideAnchor();
60253 this.el.slideOut(anchor, {
60254 callback : function(){
60255 this.el.setStyle("z-index", "");
60256 this.collapsedEl.slideIn(anchor, {duration:.3});
60258 this.el.setLocation(-10000,-10000);
60260 this.fireEvent("collapsed", this);
60267 animateExpand : function(){
60268 this.beforeSlide();
60269 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
60270 this.el.setStyle("z-index", 20000);
60271 this.collapsedEl.hide({
60274 this.el.slideIn(this.getSlideAnchor(), {
60275 callback : function(){
60276 this.el.setStyle("z-index", "");
60279 this.split.el.show();
60281 this.fireEvent("invalidated", this);
60282 this.fireEvent("expanded", this);
60310 getAnchor : function(){
60311 return this.anchors[this.position];
60314 getCollapseAnchor : function(){
60315 return this.canchors[this.position];
60318 getSlideAnchor : function(){
60319 return this.sanchors[this.position];
60322 getAlignAdj : function(){
60323 var cm = this.cmargins;
60324 switch(this.position){
60340 getExpandAdj : function(){
60341 var c = this.collapsedEl, cm = this.cmargins;
60342 switch(this.position){
60344 return [-(cm.right+c.getWidth()+cm.left), 0];
60347 return [cm.right+c.getWidth()+cm.left, 0];
60350 return [0, -(cm.top+cm.bottom+c.getHeight())];
60353 return [0, cm.top+cm.bottom+c.getHeight()];
60359 * Ext JS Library 1.1.1
60360 * Copyright(c) 2006-2007, Ext JS, LLC.
60362 * Originally Released Under LGPL - original licence link has changed is not relivant.
60365 * <script type="text/javascript">
60368 * These classes are private internal classes
60370 Roo.CenterLayoutRegion = function(mgr, config){
60371 Roo.LayoutRegion.call(this, mgr, config, "center");
60372 this.visible = true;
60373 this.minWidth = config.minWidth || 20;
60374 this.minHeight = config.minHeight || 20;
60377 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
60379 // center panel can't be hidden
60383 // center panel can't be hidden
60386 getMinWidth: function(){
60387 return this.minWidth;
60390 getMinHeight: function(){
60391 return this.minHeight;
60396 Roo.NorthLayoutRegion = function(mgr, config){
60397 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
60399 this.split.placement = Roo.SplitBar.TOP;
60400 this.split.orientation = Roo.SplitBar.VERTICAL;
60401 this.split.el.addClass("x-layout-split-v");
60403 var size = config.initialSize || config.height;
60404 if(typeof size != "undefined"){
60405 this.el.setHeight(size);
60408 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
60409 orientation: Roo.SplitBar.VERTICAL,
60410 getBox : function(){
60411 if(this.collapsed){
60412 return this.collapsedEl.getBox();
60414 var box = this.el.getBox();
60416 box.height += this.split.el.getHeight();
60421 updateBox : function(box){
60422 if(this.split && !this.collapsed){
60423 box.height -= this.split.el.getHeight();
60424 this.split.el.setLeft(box.x);
60425 this.split.el.setTop(box.y+box.height);
60426 this.split.el.setWidth(box.width);
60428 if(this.collapsed){
60429 this.updateBody(box.width, null);
60431 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60435 Roo.SouthLayoutRegion = function(mgr, config){
60436 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
60438 this.split.placement = Roo.SplitBar.BOTTOM;
60439 this.split.orientation = Roo.SplitBar.VERTICAL;
60440 this.split.el.addClass("x-layout-split-v");
60442 var size = config.initialSize || config.height;
60443 if(typeof size != "undefined"){
60444 this.el.setHeight(size);
60447 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
60448 orientation: Roo.SplitBar.VERTICAL,
60449 getBox : function(){
60450 if(this.collapsed){
60451 return this.collapsedEl.getBox();
60453 var box = this.el.getBox();
60455 var sh = this.split.el.getHeight();
60462 updateBox : function(box){
60463 if(this.split && !this.collapsed){
60464 var sh = this.split.el.getHeight();
60467 this.split.el.setLeft(box.x);
60468 this.split.el.setTop(box.y-sh);
60469 this.split.el.setWidth(box.width);
60471 if(this.collapsed){
60472 this.updateBody(box.width, null);
60474 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60478 Roo.EastLayoutRegion = function(mgr, config){
60479 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
60481 this.split.placement = Roo.SplitBar.RIGHT;
60482 this.split.orientation = Roo.SplitBar.HORIZONTAL;
60483 this.split.el.addClass("x-layout-split-h");
60485 var size = config.initialSize || config.width;
60486 if(typeof size != "undefined"){
60487 this.el.setWidth(size);
60490 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
60491 orientation: Roo.SplitBar.HORIZONTAL,
60492 getBox : function(){
60493 if(this.collapsed){
60494 return this.collapsedEl.getBox();
60496 var box = this.el.getBox();
60498 var sw = this.split.el.getWidth();
60505 updateBox : function(box){
60506 if(this.split && !this.collapsed){
60507 var sw = this.split.el.getWidth();
60509 this.split.el.setLeft(box.x);
60510 this.split.el.setTop(box.y);
60511 this.split.el.setHeight(box.height);
60514 if(this.collapsed){
60515 this.updateBody(null, box.height);
60517 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60521 Roo.WestLayoutRegion = function(mgr, config){
60522 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
60524 this.split.placement = Roo.SplitBar.LEFT;
60525 this.split.orientation = Roo.SplitBar.HORIZONTAL;
60526 this.split.el.addClass("x-layout-split-h");
60528 var size = config.initialSize || config.width;
60529 if(typeof size != "undefined"){
60530 this.el.setWidth(size);
60533 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
60534 orientation: Roo.SplitBar.HORIZONTAL,
60535 getBox : function(){
60536 if(this.collapsed){
60537 return this.collapsedEl.getBox();
60539 var box = this.el.getBox();
60541 box.width += this.split.el.getWidth();
60546 updateBox : function(box){
60547 if(this.split && !this.collapsed){
60548 var sw = this.split.el.getWidth();
60550 this.split.el.setLeft(box.x+box.width);
60551 this.split.el.setTop(box.y);
60552 this.split.el.setHeight(box.height);
60554 if(this.collapsed){
60555 this.updateBody(null, box.height);
60557 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60562 * Ext JS Library 1.1.1
60563 * Copyright(c) 2006-2007, Ext JS, LLC.
60565 * Originally Released Under LGPL - original licence link has changed is not relivant.
60568 * <script type="text/javascript">
60573 * Private internal class for reading and applying state
60575 Roo.LayoutStateManager = function(layout){
60576 // default empty state
60585 Roo.LayoutStateManager.prototype = {
60586 init : function(layout, provider){
60587 this.provider = provider;
60588 var state = provider.get(layout.id+"-layout-state");
60590 var wasUpdating = layout.isUpdating();
60592 layout.beginUpdate();
60594 for(var key in state){
60595 if(typeof state[key] != "function"){
60596 var rstate = state[key];
60597 var r = layout.getRegion(key);
60600 r.resizeTo(rstate.size);
60602 if(rstate.collapsed == true){
60605 r.expand(null, true);
60611 layout.endUpdate();
60613 this.state = state;
60615 this.layout = layout;
60616 layout.on("regionresized", this.onRegionResized, this);
60617 layout.on("regioncollapsed", this.onRegionCollapsed, this);
60618 layout.on("regionexpanded", this.onRegionExpanded, this);
60621 storeState : function(){
60622 this.provider.set(this.layout.id+"-layout-state", this.state);
60625 onRegionResized : function(region, newSize){
60626 this.state[region.getPosition()].size = newSize;
60630 onRegionCollapsed : function(region){
60631 this.state[region.getPosition()].collapsed = true;
60635 onRegionExpanded : function(region){
60636 this.state[region.getPosition()].collapsed = false;
60641 * Ext JS Library 1.1.1
60642 * Copyright(c) 2006-2007, Ext JS, LLC.
60644 * Originally Released Under LGPL - original licence link has changed is not relivant.
60647 * <script type="text/javascript">
60650 * @class Roo.ContentPanel
60651 * @extends Roo.util.Observable
60652 * @children Roo.form.Form Roo.JsonView Roo.View
60653 * @parent Roo.BorderLayout Roo.LayoutDialog builder
60654 * A basic ContentPanel element.
60655 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
60656 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
60657 * @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
60658 * @cfg {Boolean} closable True if the panel can be closed/removed
60659 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
60660 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
60661 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
60662 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
60663 * @cfg {String} title The title for this panel
60664 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
60665 * @cfg {String} url Calls {@link #setUrl} with this value
60666 * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
60667 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
60668 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
60669 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
60670 * @cfg {String} style Extra style to add to the content panel
60671 * @cfg {Roo.menu.Menu} menu popup menu
60674 * Create a new ContentPanel.
60675 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
60676 * @param {String/Object} config A string to set only the title or a config object
60677 * @param {String} content (optional) Set the HTML content for this panel
60678 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
60680 Roo.ContentPanel = function(el, config, content){
60683 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
60687 if (config && config.parentLayout) {
60688 el = config.parentLayout.el.createChild();
60691 if(el.autoCreate){ // xtype is available if this is called from factory
60695 this.el = Roo.get(el);
60696 if(!this.el && config && config.autoCreate){
60697 if(typeof config.autoCreate == "object"){
60698 if(!config.autoCreate.id){
60699 config.autoCreate.id = config.id||el;
60701 this.el = Roo.DomHelper.append(document.body,
60702 config.autoCreate, true);
60704 this.el = Roo.DomHelper.append(document.body,
60705 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
60710 this.closable = false;
60711 this.loaded = false;
60712 this.active = false;
60713 if(typeof config == "string"){
60714 this.title = config;
60716 Roo.apply(this, config);
60719 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
60720 this.wrapEl = this.el.wrap();
60721 this.toolbar.container = this.el.insertSibling(false, 'before');
60722 this.toolbar = new Roo.Toolbar(this.toolbar);
60725 // xtype created footer. - not sure if will work as we normally have to render first..
60726 if (this.footer && !this.footer.el && this.footer.xtype) {
60727 if (!this.wrapEl) {
60728 this.wrapEl = this.el.wrap();
60731 this.footer.container = this.wrapEl.createChild();
60733 this.footer = Roo.factory(this.footer, Roo);
60738 this.resizeEl = Roo.get(this.resizeEl, true);
60740 this.resizeEl = this.el;
60742 // handle view.xtype
60750 * Fires when this panel is activated.
60751 * @param {Roo.ContentPanel} this
60755 * @event deactivate
60756 * Fires when this panel is activated.
60757 * @param {Roo.ContentPanel} this
60759 "deactivate" : true,
60763 * Fires when this panel is resized if fitToFrame is true.
60764 * @param {Roo.ContentPanel} this
60765 * @param {Number} width The width after any component adjustments
60766 * @param {Number} height The height after any component adjustments
60772 * Fires when this tab is created
60773 * @param {Roo.ContentPanel} this
60783 if(this.autoScroll){
60784 this.resizeEl.setStyle("overflow", "auto");
60786 // fix randome scrolling
60787 this.el.on('scroll', function() {
60788 Roo.log('fix random scolling');
60789 this.scrollTo('top',0);
60792 content = content || this.content;
60794 this.setContent(content);
60796 if(config && config.url){
60797 this.setUrl(this.url, this.params, this.loadOnce);
60802 Roo.ContentPanel.superclass.constructor.call(this);
60804 if (this.view && typeof(this.view.xtype) != 'undefined') {
60805 this.view.el = this.el.appendChild(document.createElement("div"));
60806 this.view = Roo.factory(this.view);
60807 this.view.render && this.view.render(false, '');
60811 this.fireEvent('render', this);
60814 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
60816 setRegion : function(region){
60817 this.region = region;
60819 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
60821 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
60826 * Returns the toolbar for this Panel if one was configured.
60827 * @return {Roo.Toolbar}
60829 getToolbar : function(){
60830 return this.toolbar;
60833 setActiveState : function(active){
60834 this.active = active;
60836 this.fireEvent("deactivate", this);
60838 this.fireEvent("activate", this);
60842 * Updates this panel's element
60843 * @param {String} content The new content
60844 * @param {Boolean} loadScripts (optional) true to look for and process scripts
60846 setContent : function(content, loadScripts){
60847 this.el.update(content, loadScripts);
60850 ignoreResize : function(w, h){
60851 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
60854 this.lastSize = {width: w, height: h};
60859 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
60860 * @return {Roo.UpdateManager} The UpdateManager
60862 getUpdateManager : function(){
60863 return this.el.getUpdateManager();
60866 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
60867 * @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:
60870 url: "your-url.php",
60871 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
60872 callback: yourFunction,
60873 scope: yourObject, //(optional scope)
60876 text: "Loading...",
60881 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
60882 * 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.
60883 * @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}
60884 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
60885 * @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.
60886 * @return {Roo.ContentPanel} this
60889 var um = this.el.getUpdateManager();
60890 um.update.apply(um, arguments);
60896 * 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.
60897 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
60898 * @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)
60899 * @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)
60900 * @return {Roo.UpdateManager} The UpdateManager
60902 setUrl : function(url, params, loadOnce){
60903 if(this.refreshDelegate){
60904 this.removeListener("activate", this.refreshDelegate);
60906 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
60907 this.on("activate", this.refreshDelegate);
60908 return this.el.getUpdateManager();
60911 _handleRefresh : function(url, params, loadOnce){
60912 if(!loadOnce || !this.loaded){
60913 var updater = this.el.getUpdateManager();
60914 updater.update(url, params, this._setLoaded.createDelegate(this));
60918 _setLoaded : function(){
60919 this.loaded = true;
60923 * Returns this panel's id
60926 getId : function(){
60931 * Returns this panel's element - used by regiosn to add.
60932 * @return {Roo.Element}
60934 getEl : function(){
60935 return this.wrapEl || this.el;
60938 adjustForComponents : function(width, height)
60940 //Roo.log('adjustForComponents ');
60941 if(this.resizeEl != this.el){
60942 width -= this.el.getFrameWidth('lr');
60943 height -= this.el.getFrameWidth('tb');
60946 var te = this.toolbar.getEl();
60947 height -= te.getHeight();
60948 te.setWidth(width);
60951 var te = this.footer.getEl();
60952 //Roo.log("footer:" + te.getHeight());
60954 height -= te.getHeight();
60955 te.setWidth(width);
60959 if(this.adjustments){
60960 width += this.adjustments[0];
60961 height += this.adjustments[1];
60963 return {"width": width, "height": height};
60966 setSize : function(width, height){
60967 if(this.fitToFrame && !this.ignoreResize(width, height)){
60968 if(this.fitContainer && this.resizeEl != this.el){
60969 this.el.setSize(width, height);
60971 var size = this.adjustForComponents(width, height);
60972 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
60973 this.fireEvent('resize', this, size.width, size.height);
60978 * Returns this panel's title
60981 getTitle : function(){
60986 * Set this panel's title
60987 * @param {String} title
60989 setTitle : function(title){
60990 this.title = title;
60992 this.region.updatePanelTitle(this, title);
60997 * Returns true is this panel was configured to be closable
60998 * @return {Boolean}
61000 isClosable : function(){
61001 return this.closable;
61004 beforeSlide : function(){
61006 this.resizeEl.clip();
61009 afterSlide : function(){
61011 this.resizeEl.unclip();
61015 * Force a content refresh from the URL specified in the {@link #setUrl} method.
61016 * Will fail silently if the {@link #setUrl} method has not been called.
61017 * This does not activate the panel, just updates its content.
61019 refresh : function(){
61020 if(this.refreshDelegate){
61021 this.loaded = false;
61022 this.refreshDelegate();
61027 * Destroys this panel
61029 destroy : function(){
61030 this.el.removeAllListeners();
61031 var tempEl = document.createElement("span");
61032 tempEl.appendChild(this.el.dom);
61033 tempEl.innerHTML = "";
61039 * form - if the content panel contains a form - this is a reference to it.
61040 * @type {Roo.form.Form}
61044 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
61045 * This contains a reference to it.
61051 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
61061 * @param {Object} cfg Xtype definition of item to add.
61064 addxtype : function(cfg) {
61065 if(cfg.xtype.match(/^UploadCropbox$/)) {
61067 this.cropbox = new Roo.factory(cfg);
61069 this.cropbox.render(this.el);
61071 return this.cropbox;
61074 if (cfg.xtype.match(/^Form$/)) {
61077 //if (this.footer) {
61078 // el = this.footer.container.insertSibling(false, 'before');
61080 el = this.el.createChild();
61083 this.form = new Roo.form.Form(cfg);
61086 if ( this.form.allItems.length) {
61087 this.form.render(el.dom);
61091 // should only have one of theses..
61092 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
61093 // views.. should not be just added - used named prop 'view''
61095 cfg.el = this.el.appendChild(document.createElement("div"));
61098 var ret = new Roo.factory(cfg);
61100 ret.render && ret.render(false, ''); // render blank..
61120 * @class Roo.GridPanel
61121 * @extends Roo.ContentPanel
61122 * @parent Roo.BorderLayout Roo.LayoutDialog builder
61124 * Create a new GridPanel.
61125 * @cfg {Roo.grid.Grid} grid The grid for this panel
61127 Roo.GridPanel = function(grid, config){
61129 // universal ctor...
61130 if (typeof(grid.grid) != 'undefined') {
61132 grid = config.grid;
61134 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
61135 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
61137 this.wrapper.dom.appendChild(grid.getGridEl().dom);
61139 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
61142 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
61144 // xtype created footer. - not sure if will work as we normally have to render first..
61145 if (this.footer && !this.footer.el && this.footer.xtype) {
61147 this.footer.container = this.grid.getView().getFooterPanel(true);
61148 this.footer.dataSource = this.grid.dataSource;
61149 this.footer = Roo.factory(this.footer, Roo);
61153 grid.monitorWindowResize = false; // turn off autosizing
61154 grid.autoHeight = false;
61155 grid.autoWidth = false;
61157 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
61160 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
61161 getId : function(){
61162 return this.grid.id;
61166 * Returns the grid for this panel
61167 * @return {Roo.grid.Grid}
61169 getGrid : function(){
61173 setSize : function(width, height){
61174 if(!this.ignoreResize(width, height)){
61175 var grid = this.grid;
61176 var size = this.adjustForComponents(width, height);
61177 grid.getGridEl().setSize(size.width, size.height);
61182 beforeSlide : function(){
61183 this.grid.getView().scroller.clip();
61186 afterSlide : function(){
61187 this.grid.getView().scroller.unclip();
61190 destroy : function(){
61191 this.grid.destroy();
61193 Roo.GridPanel.superclass.destroy.call(this);
61199 * @class Roo.NestedLayoutPanel
61200 * @extends Roo.ContentPanel
61201 * @parent Roo.BorderLayout Roo.LayoutDialog builder
61202 * @cfg {Roo.BorderLayout} layout [required] The layout for this panel
61206 * Create a new NestedLayoutPanel.
61209 * @param {Roo.BorderLayout} layout [required] The layout for this panel
61210 * @param {String/Object} config A string to set only the title or a config object
61212 Roo.NestedLayoutPanel = function(layout, config)
61214 // construct with only one argument..
61215 /* FIXME - implement nicer consturctors
61216 if (layout.layout) {
61218 layout = config.layout;
61219 delete config.layout;
61221 if (layout.xtype && !layout.getEl) {
61222 // then layout needs constructing..
61223 layout = Roo.factory(layout, Roo);
61228 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
61230 layout.monitorWindowResize = false; // turn off autosizing
61231 this.layout = layout;
61232 this.layout.getEl().addClass("x-layout-nested-layout");
61239 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
61243 setSize : function(width, height){
61244 if(!this.ignoreResize(width, height)){
61245 var size = this.adjustForComponents(width, height);
61246 var el = this.layout.getEl();
61247 el.setSize(size.width, size.height);
61248 var touch = el.dom.offsetWidth;
61249 this.layout.layout();
61250 // ie requires a double layout on the first pass
61251 if(Roo.isIE && !this.initialized){
61252 this.initialized = true;
61253 this.layout.layout();
61258 // activate all subpanels if not currently active..
61260 setActiveState : function(active){
61261 this.active = active;
61263 this.fireEvent("deactivate", this);
61267 this.fireEvent("activate", this);
61268 // not sure if this should happen before or after..
61269 if (!this.layout) {
61270 return; // should not happen..
61273 for (var r in this.layout.regions) {
61274 reg = this.layout.getRegion(r);
61275 if (reg.getActivePanel()) {
61276 //reg.showPanel(reg.getActivePanel()); // force it to activate..
61277 reg.setActivePanel(reg.getActivePanel());
61280 if (!reg.panels.length) {
61283 reg.showPanel(reg.getPanel(0));
61292 * Returns the nested BorderLayout for this panel
61293 * @return {Roo.BorderLayout}
61295 getLayout : function(){
61296 return this.layout;
61300 * Adds a xtype elements to the layout of the nested panel
61304 xtype : 'ContentPanel',
61311 xtype : 'NestedLayoutPanel',
61317 items : [ ... list of content panels or nested layout panels.. ]
61321 * @param {Object} cfg Xtype definition of item to add.
61323 addxtype : function(cfg) {
61324 return this.layout.addxtype(cfg);
61329 Roo.ScrollPanel = function(el, config, content){
61330 config = config || {};
61331 config.fitToFrame = true;
61332 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
61334 this.el.dom.style.overflow = "hidden";
61335 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
61336 this.el.removeClass("x-layout-inactive-content");
61337 this.el.on("mousewheel", this.onWheel, this);
61339 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
61340 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
61341 up.unselectable(); down.unselectable();
61342 up.on("click", this.scrollUp, this);
61343 down.on("click", this.scrollDown, this);
61344 up.addClassOnOver("x-scroller-btn-over");
61345 down.addClassOnOver("x-scroller-btn-over");
61346 up.addClassOnClick("x-scroller-btn-click");
61347 down.addClassOnClick("x-scroller-btn-click");
61348 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
61350 this.resizeEl = this.el;
61351 this.el = wrap; this.up = up; this.down = down;
61354 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
61356 wheelIncrement : 5,
61357 scrollUp : function(){
61358 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
61361 scrollDown : function(){
61362 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
61365 afterScroll : function(){
61366 var el = this.resizeEl;
61367 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
61368 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61369 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61372 setSize : function(){
61373 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
61374 this.afterScroll();
61377 onWheel : function(e){
61378 var d = e.getWheelDelta();
61379 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
61380 this.afterScroll();
61384 setContent : function(content, loadScripts){
61385 this.resizeEl.update(content, loadScripts);
61393 * @class Roo.TreePanel
61394 * @extends Roo.ContentPanel
61395 * @parent Roo.BorderLayout Roo.LayoutDialog builder
61396 * Treepanel component
61399 * Create a new TreePanel. - defaults to fit/scoll contents.
61400 * @param {String/Object} config A string to set only the panel's title, or a config object
61402 Roo.TreePanel = function(config){
61403 var el = config.el;
61404 var tree = config.tree;
61405 delete config.tree;
61406 delete config.el; // hopefull!
61408 // wrapper for IE7 strict & safari scroll issue
61410 var treeEl = el.createChild();
61411 config.resizeEl = treeEl;
61415 Roo.TreePanel.superclass.constructor.call(this, el, config);
61418 this.tree = new Roo.tree.TreePanel(treeEl , tree);
61419 //console.log(tree);
61420 this.on('activate', function()
61422 if (this.tree.rendered) {
61425 //console.log('render tree');
61426 this.tree.render();
61428 // this should not be needed.. - it's actually the 'el' that resizes?
61429 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
61431 //this.on('resize', function (cp, w, h) {
61432 // this.tree.innerCt.setWidth(w);
61433 // this.tree.innerCt.setHeight(h);
61434 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
61441 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
61445 * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
61452 * Ext JS Library 1.1.1
61453 * Copyright(c) 2006-2007, Ext JS, LLC.
61455 * Originally Released Under LGPL - original licence link has changed is not relivant.
61458 * <script type="text/javascript">
61463 * @class Roo.ReaderLayout
61464 * @extends Roo.BorderLayout
61465 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
61466 * center region containing two nested regions (a top one for a list view and one for item preview below),
61467 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
61468 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
61469 * expedites the setup of the overall layout and regions for this common application style.
61472 var reader = new Roo.ReaderLayout();
61473 var CP = Roo.ContentPanel; // shortcut for adding
61475 reader.beginUpdate();
61476 reader.add("north", new CP("north", "North"));
61477 reader.add("west", new CP("west", {title: "West"}));
61478 reader.add("east", new CP("east", {title: "East"}));
61480 reader.regions.listView.add(new CP("listView", "List"));
61481 reader.regions.preview.add(new CP("preview", "Preview"));
61482 reader.endUpdate();
61485 * Create a new ReaderLayout
61486 * @param {Object} config Configuration options
61487 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
61488 * document.body if omitted)
61490 Roo.ReaderLayout = function(config, renderTo){
61491 var c = config || {size:{}};
61492 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
61493 north: c.north !== false ? Roo.apply({
61497 }, c.north) : false,
61498 west: c.west !== false ? Roo.apply({
61506 margins:{left:5,right:0,bottom:5,top:5},
61507 cmargins:{left:5,right:5,bottom:5,top:5}
61508 }, c.west) : false,
61509 east: c.east !== false ? Roo.apply({
61517 margins:{left:0,right:5,bottom:5,top:5},
61518 cmargins:{left:5,right:5,bottom:5,top:5}
61519 }, c.east) : false,
61520 center: Roo.apply({
61521 tabPosition: 'top',
61525 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
61529 this.el.addClass('x-reader');
61531 this.beginUpdate();
61533 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
61534 south: c.preview !== false ? Roo.apply({
61541 cmargins:{top:5,left:0, right:0, bottom:0}
61542 }, c.preview) : false,
61543 center: Roo.apply({
61549 this.add('center', new Roo.NestedLayoutPanel(inner,
61550 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
61554 this.regions.preview = inner.getRegion('south');
61555 this.regions.listView = inner.getRegion('center');
61558 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
61560 * Ext JS Library 1.1.1
61561 * Copyright(c) 2006-2007, Ext JS, LLC.
61563 * Originally Released Under LGPL - original licence link has changed is not relivant.
61566 * <script type="text/javascript">
61570 * @class Roo.grid.Grid
61571 * @extends Roo.util.Observable
61572 * This class represents the primary interface of a component based grid control.
61573 * <br><br>Usage:<pre><code>
61574 var grid = new Roo.grid.Grid("my-container-id", {
61577 selModel: mySelectionModel,
61578 autoSizeColumns: true,
61579 monitorWindowResize: false,
61580 trackMouseOver: true
61585 * <b>Common Problems:</b><br/>
61586 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
61587 * element will correct this<br/>
61588 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
61589 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
61590 * are unpredictable.<br/>
61591 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
61592 * grid to calculate dimensions/offsets.<br/>
61594 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61595 * The container MUST have some type of size defined for the grid to fill. The container will be
61596 * automatically set to position relative if it isn't already.
61597 * @param {Object} config A config object that sets properties on this grid.
61599 Roo.grid.Grid = function(container, config){
61600 // initialize the container
61601 this.container = Roo.get(container);
61602 this.container.update("");
61603 this.container.setStyle("overflow", "hidden");
61604 this.container.addClass('x-grid-container');
61606 this.id = this.container.id;
61608 Roo.apply(this, config);
61609 // check and correct shorthanded configs
61611 this.dataSource = this.ds;
61615 this.colModel = this.cm;
61619 this.selModel = this.sm;
61623 if (this.selModel) {
61624 this.selModel = Roo.factory(this.selModel, Roo.grid);
61625 this.sm = this.selModel;
61626 this.sm.xmodule = this.xmodule || false;
61628 if (typeof(this.colModel.config) == 'undefined') {
61629 this.colModel = new Roo.grid.ColumnModel(this.colModel);
61630 this.cm = this.colModel;
61631 this.cm.xmodule = this.xmodule || false;
61633 if (this.dataSource) {
61634 this.dataSource= Roo.factory(this.dataSource, Roo.data);
61635 this.ds = this.dataSource;
61636 this.ds.xmodule = this.xmodule || false;
61643 this.container.setWidth(this.width);
61647 this.container.setHeight(this.height);
61654 * The raw click event for the entire grid.
61655 * @param {Roo.EventObject} e
61660 * The raw dblclick event for the entire grid.
61661 * @param {Roo.EventObject} e
61665 * @event contextmenu
61666 * The raw contextmenu event for the entire grid.
61667 * @param {Roo.EventObject} e
61669 "contextmenu" : true,
61672 * The raw mousedown event for the entire grid.
61673 * @param {Roo.EventObject} e
61675 "mousedown" : true,
61678 * The raw mouseup event for the entire grid.
61679 * @param {Roo.EventObject} e
61684 * The raw mouseover event for the entire grid.
61685 * @param {Roo.EventObject} e
61687 "mouseover" : true,
61690 * The raw mouseout event for the entire grid.
61691 * @param {Roo.EventObject} e
61696 * The raw keypress event for the entire grid.
61697 * @param {Roo.EventObject} e
61702 * The raw keydown event for the entire grid.
61703 * @param {Roo.EventObject} e
61711 * Fires when a cell is clicked
61712 * @param {Grid} this
61713 * @param {Number} rowIndex
61714 * @param {Number} columnIndex
61715 * @param {Roo.EventObject} e
61717 "cellclick" : true,
61719 * @event celldblclick
61720 * Fires when a cell is double clicked
61721 * @param {Grid} this
61722 * @param {Number} rowIndex
61723 * @param {Number} columnIndex
61724 * @param {Roo.EventObject} e
61726 "celldblclick" : true,
61729 * Fires when a row is clicked
61730 * @param {Grid} this
61731 * @param {Number} rowIndex
61732 * @param {Roo.EventObject} e
61736 * @event rowdblclick
61737 * Fires when a row is double clicked
61738 * @param {Grid} this
61739 * @param {Number} rowIndex
61740 * @param {Roo.EventObject} e
61742 "rowdblclick" : true,
61744 * @event headerclick
61745 * Fires when a header is clicked
61746 * @param {Grid} this
61747 * @param {Number} columnIndex
61748 * @param {Roo.EventObject} e
61750 "headerclick" : true,
61752 * @event headerdblclick
61753 * Fires when a header cell is double clicked
61754 * @param {Grid} this
61755 * @param {Number} columnIndex
61756 * @param {Roo.EventObject} e
61758 "headerdblclick" : true,
61760 * @event rowcontextmenu
61761 * Fires when a row is right clicked
61762 * @param {Grid} this
61763 * @param {Number} rowIndex
61764 * @param {Roo.EventObject} e
61766 "rowcontextmenu" : true,
61768 * @event cellcontextmenu
61769 * Fires when a cell is right clicked
61770 * @param {Grid} this
61771 * @param {Number} rowIndex
61772 * @param {Number} cellIndex
61773 * @param {Roo.EventObject} e
61775 "cellcontextmenu" : true,
61777 * @event headercontextmenu
61778 * Fires when a header is right clicked
61779 * @param {Grid} this
61780 * @param {Number} columnIndex
61781 * @param {Roo.EventObject} e
61783 "headercontextmenu" : true,
61785 * @event bodyscroll
61786 * Fires when the body element is scrolled
61787 * @param {Number} scrollLeft
61788 * @param {Number} scrollTop
61790 "bodyscroll" : true,
61792 * @event columnresize
61793 * Fires when the user resizes a column
61794 * @param {Number} columnIndex
61795 * @param {Number} newSize
61797 "columnresize" : true,
61799 * @event columnmove
61800 * Fires when the user moves a column
61801 * @param {Number} oldIndex
61802 * @param {Number} newIndex
61804 "columnmove" : true,
61807 * Fires when row(s) start being dragged
61808 * @param {Grid} this
61809 * @param {Roo.GridDD} dd The drag drop object
61810 * @param {event} e The raw browser event
61812 "startdrag" : true,
61815 * Fires when a drag operation is complete
61816 * @param {Grid} this
61817 * @param {Roo.GridDD} dd The drag drop object
61818 * @param {event} e The raw browser event
61823 * Fires when dragged row(s) are dropped on a valid DD target
61824 * @param {Grid} this
61825 * @param {Roo.GridDD} dd The drag drop object
61826 * @param {String} targetId The target drag drop object
61827 * @param {event} e The raw browser event
61832 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61833 * @param {Grid} this
61834 * @param {Roo.GridDD} dd The drag drop object
61835 * @param {String} targetId The target drag drop object
61836 * @param {event} e The raw browser event
61841 * Fires when the dragged row(s) first cross another DD target while being dragged
61842 * @param {Grid} this
61843 * @param {Roo.GridDD} dd The drag drop object
61844 * @param {String} targetId The target drag drop object
61845 * @param {event} e The raw browser event
61847 "dragenter" : true,
61850 * Fires when the dragged row(s) leave another DD target while being dragged
61851 * @param {Grid} this
61852 * @param {Roo.GridDD} dd The drag drop object
61853 * @param {String} targetId The target drag drop object
61854 * @param {event} e The raw browser event
61859 * Fires when a row is rendered, so you can change add a style to it.
61860 * @param {GridView} gridview The grid view
61861 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
61867 * Fires when the grid is rendered
61868 * @param {Grid} grid
61873 Roo.grid.Grid.superclass.constructor.call(this);
61875 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
61878 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
61881 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
61884 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
61887 * @cfg {Roo.data.Store} ds The data store for the grid
61890 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
61893 * @cfg {String} ddGroup - drag drop group.
61896 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
61900 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
61902 minColumnWidth : 25,
61905 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
61906 * <b>on initial render.</b> It is more efficient to explicitly size the columns
61907 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
61909 autoSizeColumns : false,
61912 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
61914 autoSizeHeaders : true,
61917 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
61919 monitorWindowResize : true,
61922 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
61923 * rows measured to get a columns size. Default is 0 (all rows).
61925 maxRowsToMeasure : 0,
61928 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
61930 trackMouseOver : true,
61933 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
61936 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
61940 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
61942 enableDragDrop : false,
61945 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
61947 enableColumnMove : true,
61950 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
61952 enableColumnHide : true,
61955 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
61957 enableRowHeightSync : false,
61960 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
61965 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
61967 autoHeight : false,
61970 * @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.
61972 autoExpandColumn : false,
61975 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
61978 autoExpandMin : 50,
61981 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
61983 autoExpandMax : 1000,
61986 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
61991 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
61995 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
61999 * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
62001 sortColMenu : false,
62007 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
62008 * of a fixed width. Default is false.
62011 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
62016 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
62017 * %0 is replaced with the number of selected rows.
62019 ddText : "{0} selected row{1}",
62023 * Called once after all setup has been completed and the grid is ready to be rendered.
62024 * @return {Roo.grid.Grid} this
62026 render : function()
62028 var c = this.container;
62029 // try to detect autoHeight/width mode
62030 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
62031 this.autoHeight = true;
62033 var view = this.getView();
62036 c.on("click", this.onClick, this);
62037 c.on("dblclick", this.onDblClick, this);
62038 c.on("contextmenu", this.onContextMenu, this);
62039 c.on("keydown", this.onKeyDown, this);
62041 c.on("touchstart", this.onTouchStart, this);
62044 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
62046 this.getSelectionModel().init(this);
62051 this.loadMask = new Roo.LoadMask(this.container,
62052 Roo.apply({store:this.dataSource}, this.loadMask));
62056 if (this.toolbar && this.toolbar.xtype) {
62057 this.toolbar.container = this.getView().getHeaderPanel(true);
62058 this.toolbar = new Roo.Toolbar(this.toolbar);
62060 if (this.footer && this.footer.xtype) {
62061 this.footer.dataSource = this.getDataSource();
62062 this.footer.container = this.getView().getFooterPanel(true);
62063 this.footer = Roo.factory(this.footer, Roo);
62065 if (this.dropTarget && this.dropTarget.xtype) {
62066 delete this.dropTarget.xtype;
62067 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
62071 this.rendered = true;
62072 this.fireEvent('render', this);
62077 * Reconfigures the grid to use a different Store and Column Model.
62078 * The View will be bound to the new objects and refreshed.
62079 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
62080 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
62082 reconfigure : function(dataSource, colModel){
62084 this.loadMask.destroy();
62085 this.loadMask = new Roo.LoadMask(this.container,
62086 Roo.apply({store:dataSource}, this.loadMask));
62088 this.view.bind(dataSource, colModel);
62089 this.dataSource = dataSource;
62090 this.colModel = colModel;
62091 this.view.refresh(true);
62095 * Add's a column, default at the end..
62097 * @param {int} position to add (default end)
62098 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
62100 addColumns : function(pos, ar)
62103 for (var i =0;i< ar.length;i++) {
62105 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
62106 this.cm.lookup[cfg.id] = cfg;
62110 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
62111 pos = this.cm.config.length; //this.cm.config.push(cfg);
62113 pos = Math.max(0,pos);
62116 this.cm.config.splice.apply(this.cm.config, ar);
62120 this.view.generateRules(this.cm);
62121 this.view.refresh(true);
62129 onKeyDown : function(e){
62130 this.fireEvent("keydown", e);
62134 * Destroy this grid.
62135 * @param {Boolean} removeEl True to remove the element
62137 destroy : function(removeEl, keepListeners){
62139 this.loadMask.destroy();
62141 var c = this.container;
62142 c.removeAllListeners();
62143 this.view.destroy();
62144 this.colModel.purgeListeners();
62145 if(!keepListeners){
62146 this.purgeListeners();
62149 if(removeEl === true){
62155 processEvent : function(name, e){
62156 // does this fire select???
62157 //Roo.log('grid:processEvent ' + name);
62159 if (name != 'touchstart' ) {
62160 this.fireEvent(name, e);
62163 var t = e.getTarget();
62165 var header = v.findHeaderIndex(t);
62166 if(header !== false){
62167 var ename = name == 'touchstart' ? 'click' : name;
62169 this.fireEvent("header" + ename, this, header, e);
62171 var row = v.findRowIndex(t);
62172 var cell = v.findCellIndex(t);
62173 if (name == 'touchstart') {
62174 // first touch is always a click.
62175 // hopefull this happens after selection is updated.?
62178 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
62179 var cs = this.selModel.getSelectedCell();
62180 if (row == cs[0] && cell == cs[1]){
62184 if (typeof(this.selModel.getSelections) != 'undefined') {
62185 var cs = this.selModel.getSelections();
62186 var ds = this.dataSource;
62187 if (cs.length == 1 && ds.getAt(row) == cs[0]){
62198 this.fireEvent("row" + name, this, row, e);
62199 if(cell !== false){
62200 this.fireEvent("cell" + name, this, row, cell, e);
62207 onClick : function(e){
62208 this.processEvent("click", e);
62211 onTouchStart : function(e){
62212 this.processEvent("touchstart", e);
62216 onContextMenu : function(e, t){
62217 this.processEvent("contextmenu", e);
62221 onDblClick : function(e){
62222 this.processEvent("dblclick", e);
62226 walkCells : function(row, col, step, fn, scope){
62227 var cm = this.colModel, clen = cm.getColumnCount();
62228 var ds = this.dataSource, rlen = ds.getCount(), first = true;
62240 if(fn.call(scope || this, row, col, cm) === true){
62258 if(fn.call(scope || this, row, col, cm) === true){
62270 getSelections : function(){
62271 return this.selModel.getSelections();
62275 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
62276 * but if manual update is required this method will initiate it.
62278 autoSize : function(){
62280 this.view.layout();
62281 if(this.view.adjustForScroll){
62282 this.view.adjustForScroll();
62288 * Returns the grid's underlying element.
62289 * @return {Element} The element
62291 getGridEl : function(){
62292 return this.container;
62295 // private for compatibility, overridden by editor grid
62296 stopEditing : function(){},
62299 * Returns the grid's SelectionModel.
62300 * @return {SelectionModel}
62302 getSelectionModel : function(){
62303 if(!this.selModel){
62304 this.selModel = new Roo.grid.RowSelectionModel();
62306 return this.selModel;
62310 * Returns the grid's DataSource.
62311 * @return {DataSource}
62313 getDataSource : function(){
62314 return this.dataSource;
62318 * Returns the grid's ColumnModel.
62319 * @return {ColumnModel}
62321 getColumnModel : function(){
62322 return this.colModel;
62326 * Returns the grid's GridView object.
62327 * @return {GridView}
62329 getView : function(){
62331 this.view = new Roo.grid.GridView(this.viewConfig);
62332 this.relayEvents(this.view, [
62333 "beforerowremoved", "beforerowsinserted",
62334 "beforerefresh", "rowremoved",
62335 "rowsinserted", "rowupdated" ,"refresh"
62341 * Called to get grid's drag proxy text, by default returns this.ddText.
62342 * Override this to put something different in the dragged text.
62345 getDragDropText : function(){
62346 var count = this.selModel.getCount();
62347 return String.format(this.ddText, count, count == 1 ? '' : 's');
62352 * Ext JS Library 1.1.1
62353 * Copyright(c) 2006-2007, Ext JS, LLC.
62355 * Originally Released Under LGPL - original licence link has changed is not relivant.
62358 * <script type="text/javascript">
62361 * @class Roo.grid.AbstractGridView
62362 * @extends Roo.util.Observable
62364 * Abstract base class for grid Views
62367 Roo.grid.AbstractGridView = function(){
62371 "beforerowremoved" : true,
62372 "beforerowsinserted" : true,
62373 "beforerefresh" : true,
62374 "rowremoved" : true,
62375 "rowsinserted" : true,
62376 "rowupdated" : true,
62379 Roo.grid.AbstractGridView.superclass.constructor.call(this);
62382 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
62383 rowClass : "x-grid-row",
62384 cellClass : "x-grid-cell",
62385 tdClass : "x-grid-td",
62386 hdClass : "x-grid-hd",
62387 splitClass : "x-grid-hd-split",
62389 init: function(grid){
62391 var cid = this.grid.getGridEl().id;
62392 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
62393 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
62394 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
62395 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
62398 getColumnRenderers : function(){
62399 var renderers = [];
62400 var cm = this.grid.colModel;
62401 var colCount = cm.getColumnCount();
62402 for(var i = 0; i < colCount; i++){
62403 renderers[i] = cm.getRenderer(i);
62408 getColumnIds : function(){
62410 var cm = this.grid.colModel;
62411 var colCount = cm.getColumnCount();
62412 for(var i = 0; i < colCount; i++){
62413 ids[i] = cm.getColumnId(i);
62418 getDataIndexes : function(){
62419 if(!this.indexMap){
62420 this.indexMap = this.buildIndexMap();
62422 return this.indexMap.colToData;
62425 getColumnIndexByDataIndex : function(dataIndex){
62426 if(!this.indexMap){
62427 this.indexMap = this.buildIndexMap();
62429 return this.indexMap.dataToCol[dataIndex];
62433 * Set a css style for a column dynamically.
62434 * @param {Number} colIndex The index of the column
62435 * @param {String} name The css property name
62436 * @param {String} value The css value
62438 setCSSStyle : function(colIndex, name, value){
62439 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
62440 Roo.util.CSS.updateRule(selector, name, value);
62443 generateRules : function(cm){
62444 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
62445 Roo.util.CSS.removeStyleSheet(rulesId);
62446 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62447 var cid = cm.getColumnId(i);
62448 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
62449 this.tdSelector, cid, " {\n}\n",
62450 this.hdSelector, cid, " {\n}\n",
62451 this.splitSelector, cid, " {\n}\n");
62453 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
62457 * Ext JS Library 1.1.1
62458 * Copyright(c) 2006-2007, Ext JS, LLC.
62460 * Originally Released Under LGPL - original licence link has changed is not relivant.
62463 * <script type="text/javascript">
62467 // This is a support class used internally by the Grid components
62468 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
62470 this.view = grid.getView();
62471 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62472 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
62474 this.setHandleElId(Roo.id(hd));
62475 this.setOuterHandleElId(Roo.id(hd2));
62477 this.scroll = false;
62479 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
62481 getDragData : function(e){
62482 var t = Roo.lib.Event.getTarget(e);
62483 var h = this.view.findHeaderCell(t);
62485 return {ddel: h.firstChild, header:h};
62490 onInitDrag : function(e){
62491 this.view.headersDisabled = true;
62492 var clone = this.dragData.ddel.cloneNode(true);
62493 clone.id = Roo.id();
62494 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
62495 this.proxy.update(clone);
62499 afterValidDrop : function(){
62501 setTimeout(function(){
62502 v.headersDisabled = false;
62506 afterInvalidDrop : function(){
62508 setTimeout(function(){
62509 v.headersDisabled = false;
62515 * Ext JS Library 1.1.1
62516 * Copyright(c) 2006-2007, Ext JS, LLC.
62518 * Originally Released Under LGPL - original licence link has changed is not relivant.
62521 * <script type="text/javascript">
62524 // This is a support class used internally by the Grid components
62525 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
62527 this.view = grid.getView();
62528 // split the proxies so they don't interfere with mouse events
62529 this.proxyTop = Roo.DomHelper.append(document.body, {
62530 cls:"col-move-top", html:" "
62532 this.proxyBottom = Roo.DomHelper.append(document.body, {
62533 cls:"col-move-bottom", html:" "
62535 this.proxyTop.hide = this.proxyBottom.hide = function(){
62536 this.setLeftTop(-100,-100);
62537 this.setStyle("visibility", "hidden");
62539 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62540 // temporarily disabled
62541 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
62542 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
62544 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
62545 proxyOffsets : [-4, -9],
62546 fly: Roo.Element.fly,
62548 getTargetFromEvent : function(e){
62549 var t = Roo.lib.Event.getTarget(e);
62550 var cindex = this.view.findCellIndex(t);
62551 if(cindex !== false){
62552 return this.view.getHeaderCell(cindex);
62557 nextVisible : function(h){
62558 var v = this.view, cm = this.grid.colModel;
62561 if(!cm.isHidden(v.getCellIndex(h))){
62569 prevVisible : function(h){
62570 var v = this.view, cm = this.grid.colModel;
62573 if(!cm.isHidden(v.getCellIndex(h))){
62581 positionIndicator : function(h, n, e){
62582 var x = Roo.lib.Event.getPageX(e);
62583 var r = Roo.lib.Dom.getRegion(n.firstChild);
62584 var px, pt, py = r.top + this.proxyOffsets[1];
62585 if((r.right - x) <= (r.right-r.left)/2){
62586 px = r.right+this.view.borderWidth;
62592 var oldIndex = this.view.getCellIndex(h);
62593 var newIndex = this.view.getCellIndex(n);
62595 if(this.grid.colModel.isFixed(newIndex)){
62599 var locked = this.grid.colModel.isLocked(newIndex);
62604 if(oldIndex < newIndex){
62607 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
62610 px += this.proxyOffsets[0];
62611 this.proxyTop.setLeftTop(px, py);
62612 this.proxyTop.show();
62613 if(!this.bottomOffset){
62614 this.bottomOffset = this.view.mainHd.getHeight();
62616 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
62617 this.proxyBottom.show();
62621 onNodeEnter : function(n, dd, e, data){
62622 if(data.header != n){
62623 this.positionIndicator(data.header, n, e);
62627 onNodeOver : function(n, dd, e, data){
62628 var result = false;
62629 if(data.header != n){
62630 result = this.positionIndicator(data.header, n, e);
62633 this.proxyTop.hide();
62634 this.proxyBottom.hide();
62636 return result ? this.dropAllowed : this.dropNotAllowed;
62639 onNodeOut : function(n, dd, e, data){
62640 this.proxyTop.hide();
62641 this.proxyBottom.hide();
62644 onNodeDrop : function(n, dd, e, data){
62645 var h = data.header;
62647 var cm = this.grid.colModel;
62648 var x = Roo.lib.Event.getPageX(e);
62649 var r = Roo.lib.Dom.getRegion(n.firstChild);
62650 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
62651 var oldIndex = this.view.getCellIndex(h);
62652 var newIndex = this.view.getCellIndex(n);
62653 var locked = cm.isLocked(newIndex);
62657 if(oldIndex < newIndex){
62660 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
62663 cm.setLocked(oldIndex, locked, true);
62664 cm.moveColumn(oldIndex, newIndex);
62665 this.grid.fireEvent("columnmove", oldIndex, newIndex);
62673 * Ext JS Library 1.1.1
62674 * Copyright(c) 2006-2007, Ext JS, LLC.
62676 * Originally Released Under LGPL - original licence link has changed is not relivant.
62679 * <script type="text/javascript">
62683 * @class Roo.grid.GridView
62684 * @extends Roo.util.Observable
62687 * @param {Object} config
62689 Roo.grid.GridView = function(config){
62690 Roo.grid.GridView.superclass.constructor.call(this);
62693 Roo.apply(this, config);
62696 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
62698 unselectable : 'unselectable="on"',
62699 unselectableCls : 'x-unselectable',
62702 rowClass : "x-grid-row",
62704 cellClass : "x-grid-col",
62706 tdClass : "x-grid-td",
62708 hdClass : "x-grid-hd",
62710 splitClass : "x-grid-split",
62712 sortClasses : ["sort-asc", "sort-desc"],
62714 enableMoveAnim : false,
62718 dh : Roo.DomHelper,
62720 fly : Roo.Element.fly,
62722 css : Roo.util.CSS,
62728 scrollIncrement : 22,
62730 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
62732 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
62734 bind : function(ds, cm){
62736 this.ds.un("load", this.onLoad, this);
62737 this.ds.un("datachanged", this.onDataChange, this);
62738 this.ds.un("add", this.onAdd, this);
62739 this.ds.un("remove", this.onRemove, this);
62740 this.ds.un("update", this.onUpdate, this);
62741 this.ds.un("clear", this.onClear, this);
62744 ds.on("load", this.onLoad, this);
62745 ds.on("datachanged", this.onDataChange, this);
62746 ds.on("add", this.onAdd, this);
62747 ds.on("remove", this.onRemove, this);
62748 ds.on("update", this.onUpdate, this);
62749 ds.on("clear", this.onClear, this);
62754 this.cm.un("widthchange", this.onColWidthChange, this);
62755 this.cm.un("headerchange", this.onHeaderChange, this);
62756 this.cm.un("hiddenchange", this.onHiddenChange, this);
62757 this.cm.un("columnmoved", this.onColumnMove, this);
62758 this.cm.un("columnlockchange", this.onColumnLock, this);
62761 this.generateRules(cm);
62762 cm.on("widthchange", this.onColWidthChange, this);
62763 cm.on("headerchange", this.onHeaderChange, this);
62764 cm.on("hiddenchange", this.onHiddenChange, this);
62765 cm.on("columnmoved", this.onColumnMove, this);
62766 cm.on("columnlockchange", this.onColumnLock, this);
62771 init: function(grid){
62772 Roo.grid.GridView.superclass.init.call(this, grid);
62774 this.bind(grid.dataSource, grid.colModel);
62776 grid.on("headerclick", this.handleHeaderClick, this);
62778 if(grid.trackMouseOver){
62779 grid.on("mouseover", this.onRowOver, this);
62780 grid.on("mouseout", this.onRowOut, this);
62782 grid.cancelTextSelection = function(){};
62783 this.gridId = grid.id;
62785 var tpls = this.templates || {};
62788 tpls.master = new Roo.Template(
62789 '<div class="x-grid" hidefocus="true">',
62790 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
62791 '<div class="x-grid-topbar"></div>',
62792 '<div class="x-grid-scroller"><div></div></div>',
62793 '<div class="x-grid-locked">',
62794 '<div class="x-grid-header">{lockedHeader}</div>',
62795 '<div class="x-grid-body">{lockedBody}</div>',
62797 '<div class="x-grid-viewport">',
62798 '<div class="x-grid-header">{header}</div>',
62799 '<div class="x-grid-body">{body}</div>',
62801 '<div class="x-grid-bottombar"></div>',
62803 '<div class="x-grid-resize-proxy"> </div>',
62806 tpls.master.disableformats = true;
62810 tpls.header = new Roo.Template(
62811 '<table border="0" cellspacing="0" cellpadding="0">',
62812 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
62815 tpls.header.disableformats = true;
62817 tpls.header.compile();
62820 tpls.hcell = new Roo.Template(
62821 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
62822 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
62825 tpls.hcell.disableFormats = true;
62827 tpls.hcell.compile();
62830 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
62831 this.unselectableCls + '" ' + this.unselectable +'> </div>');
62832 tpls.hsplit.disableFormats = true;
62834 tpls.hsplit.compile();
62837 tpls.body = new Roo.Template(
62838 '<table border="0" cellspacing="0" cellpadding="0">',
62839 "<tbody>{rows}</tbody>",
62842 tpls.body.disableFormats = true;
62844 tpls.body.compile();
62847 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
62848 tpls.row.disableFormats = true;
62850 tpls.row.compile();
62853 tpls.cell = new Roo.Template(
62854 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
62855 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
62856 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
62859 tpls.cell.disableFormats = true;
62861 tpls.cell.compile();
62863 this.templates = tpls;
62866 // remap these for backwards compat
62867 onColWidthChange : function(){
62868 this.updateColumns.apply(this, arguments);
62870 onHeaderChange : function(){
62871 this.updateHeaders.apply(this, arguments);
62873 onHiddenChange : function(){
62874 this.handleHiddenChange.apply(this, arguments);
62876 onColumnMove : function(){
62877 this.handleColumnMove.apply(this, arguments);
62879 onColumnLock : function(){
62880 this.handleLockChange.apply(this, arguments);
62883 onDataChange : function(){
62885 this.updateHeaderSortState();
62888 onClear : function(){
62892 onUpdate : function(ds, record){
62893 this.refreshRow(record);
62896 refreshRow : function(record){
62897 var ds = this.ds, index;
62898 if(typeof record == 'number'){
62900 record = ds.getAt(index);
62902 index = ds.indexOf(record);
62904 this.insertRows(ds, index, index, true);
62905 this.onRemove(ds, record, index+1, true);
62906 this.syncRowHeights(index, index);
62908 this.fireEvent("rowupdated", this, index, record);
62911 onAdd : function(ds, records, index){
62912 this.insertRows(ds, index, index + (records.length-1));
62915 onRemove : function(ds, record, index, isUpdate){
62916 if(isUpdate !== true){
62917 this.fireEvent("beforerowremoved", this, index, record);
62919 var bt = this.getBodyTable(), lt = this.getLockedTable();
62920 if(bt.rows[index]){
62921 bt.firstChild.removeChild(bt.rows[index]);
62923 if(lt.rows[index]){
62924 lt.firstChild.removeChild(lt.rows[index]);
62926 if(isUpdate !== true){
62927 this.stripeRows(index);
62928 this.syncRowHeights(index, index);
62930 this.fireEvent("rowremoved", this, index, record);
62934 onLoad : function(){
62935 this.scrollToTop();
62939 * Scrolls the grid to the top
62941 scrollToTop : function(){
62943 this.scroller.dom.scrollTop = 0;
62949 * Gets a panel in the header of the grid that can be used for toolbars etc.
62950 * After modifying the contents of this panel a call to grid.autoSize() may be
62951 * required to register any changes in size.
62952 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
62953 * @return Roo.Element
62955 getHeaderPanel : function(doShow){
62957 this.headerPanel.show();
62959 return this.headerPanel;
62963 * Gets a panel in the footer of the grid that can be used for toolbars etc.
62964 * After modifying the contents of this panel a call to grid.autoSize() may be
62965 * required to register any changes in size.
62966 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
62967 * @return Roo.Element
62969 getFooterPanel : function(doShow){
62971 this.footerPanel.show();
62973 return this.footerPanel;
62976 initElements : function(){
62977 var E = Roo.Element;
62978 var el = this.grid.getGridEl().dom.firstChild;
62979 var cs = el.childNodes;
62981 this.el = new E(el);
62983 this.focusEl = new E(el.firstChild);
62984 this.focusEl.swallowEvent("click", true);
62986 this.headerPanel = new E(cs[1]);
62987 this.headerPanel.enableDisplayMode("block");
62989 this.scroller = new E(cs[2]);
62990 this.scrollSizer = new E(this.scroller.dom.firstChild);
62992 this.lockedWrap = new E(cs[3]);
62993 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
62994 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
62996 this.mainWrap = new E(cs[4]);
62997 this.mainHd = new E(this.mainWrap.dom.firstChild);
62998 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
63000 this.footerPanel = new E(cs[5]);
63001 this.footerPanel.enableDisplayMode("block");
63003 this.resizeProxy = new E(cs[6]);
63005 this.headerSelector = String.format(
63006 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
63007 this.lockedHd.id, this.mainHd.id
63010 this.splitterSelector = String.format(
63011 '#{0} div.x-grid-split, #{1} div.x-grid-split',
63012 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
63015 idToCssName : function(s)
63017 return s.replace(/[^a-z0-9]+/ig, '-');
63020 getHeaderCell : function(index){
63021 return Roo.DomQuery.select(this.headerSelector)[index];
63024 getHeaderCellMeasure : function(index){
63025 return this.getHeaderCell(index).firstChild;
63028 getHeaderCellText : function(index){
63029 return this.getHeaderCell(index).firstChild.firstChild;
63032 getLockedTable : function(){
63033 return this.lockedBody.dom.firstChild;
63036 getBodyTable : function(){
63037 return this.mainBody.dom.firstChild;
63040 getLockedRow : function(index){
63041 return this.getLockedTable().rows[index];
63044 getRow : function(index){
63045 return this.getBodyTable().rows[index];
63048 getRowComposite : function(index){
63050 this.rowEl = new Roo.CompositeElementLite();
63052 var els = [], lrow, mrow;
63053 if(lrow = this.getLockedRow(index)){
63056 if(mrow = this.getRow(index)){
63059 this.rowEl.elements = els;
63063 * Gets the 'td' of the cell
63065 * @param {Integer} rowIndex row to select
63066 * @param {Integer} colIndex column to select
63070 getCell : function(rowIndex, colIndex){
63071 var locked = this.cm.getLockedCount();
63073 if(colIndex < locked){
63074 source = this.lockedBody.dom.firstChild;
63076 source = this.mainBody.dom.firstChild;
63077 colIndex -= locked;
63079 return source.rows[rowIndex].childNodes[colIndex];
63082 getCellText : function(rowIndex, colIndex){
63083 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
63086 getCellBox : function(cell){
63087 var b = this.fly(cell).getBox();
63088 if(Roo.isOpera){ // opera fails to report the Y
63089 b.y = cell.offsetTop + this.mainBody.getY();
63094 getCellIndex : function(cell){
63095 var id = String(cell.className).match(this.cellRE);
63097 return parseInt(id[1], 10);
63102 findHeaderIndex : function(n){
63103 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
63104 return r ? this.getCellIndex(r) : false;
63107 findHeaderCell : function(n){
63108 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
63109 return r ? r : false;
63112 findRowIndex : function(n){
63116 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
63117 return r ? r.rowIndex : false;
63120 findCellIndex : function(node){
63121 var stop = this.el.dom;
63122 while(node && node != stop){
63123 if(this.findRE.test(node.className)){
63124 return this.getCellIndex(node);
63126 node = node.parentNode;
63131 getColumnId : function(index){
63132 return this.cm.getColumnId(index);
63135 getSplitters : function()
63137 if(this.splitterSelector){
63138 return Roo.DomQuery.select(this.splitterSelector);
63144 getSplitter : function(index){
63145 return this.getSplitters()[index];
63148 onRowOver : function(e, t){
63150 if((row = this.findRowIndex(t)) !== false){
63151 this.getRowComposite(row).addClass("x-grid-row-over");
63155 onRowOut : function(e, t){
63157 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
63158 this.getRowComposite(row).removeClass("x-grid-row-over");
63162 renderHeaders : function(){
63164 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
63165 var cb = [], lb = [], sb = [], lsb = [], p = {};
63166 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63167 p.cellId = "x-grid-hd-0-" + i;
63168 p.splitId = "x-grid-csplit-0-" + i;
63169 p.id = cm.getColumnId(i);
63170 p.value = cm.getColumnHeader(i) || "";
63171 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
63172 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
63173 if(!cm.isLocked(i)){
63174 cb[cb.length] = ct.apply(p);
63175 sb[sb.length] = st.apply(p);
63177 lb[lb.length] = ct.apply(p);
63178 lsb[lsb.length] = st.apply(p);
63181 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
63182 ht.apply({cells: cb.join(""), splits:sb.join("")})];
63185 updateHeaders : function(){
63186 var html = this.renderHeaders();
63187 this.lockedHd.update(html[0]);
63188 this.mainHd.update(html[1]);
63192 * Focuses the specified row.
63193 * @param {Number} row The row index
63195 focusRow : function(row)
63197 //Roo.log('GridView.focusRow');
63198 var x = this.scroller.dom.scrollLeft;
63199 this.focusCell(row, 0, false);
63200 this.scroller.dom.scrollLeft = x;
63204 * Focuses the specified cell.
63205 * @param {Number} row The row index
63206 * @param {Number} col The column index
63207 * @param {Boolean} hscroll false to disable horizontal scrolling
63209 focusCell : function(row, col, hscroll)
63211 //Roo.log('GridView.focusCell');
63212 var el = this.ensureVisible(row, col, hscroll);
63213 this.focusEl.alignTo(el, "tl-tl");
63215 this.focusEl.focus();
63217 this.focusEl.focus.defer(1, this.focusEl);
63222 * Scrolls the specified cell into view
63223 * @param {Number} row The row index
63224 * @param {Number} col The column index
63225 * @param {Boolean} hscroll false to disable horizontal scrolling
63227 ensureVisible : function(row, col, hscroll)
63229 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
63230 //return null; //disable for testing.
63231 if(typeof row != "number"){
63232 row = row.rowIndex;
63234 if(row < 0 && row >= this.ds.getCount()){
63237 col = (col !== undefined ? col : 0);
63238 var cm = this.grid.colModel;
63239 while(cm.isHidden(col)){
63243 var el = this.getCell(row, col);
63247 var c = this.scroller.dom;
63249 var ctop = parseInt(el.offsetTop, 10);
63250 var cleft = parseInt(el.offsetLeft, 10);
63251 var cbot = ctop + el.offsetHeight;
63252 var cright = cleft + el.offsetWidth;
63254 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
63255 var stop = parseInt(c.scrollTop, 10);
63256 var sleft = parseInt(c.scrollLeft, 10);
63257 var sbot = stop + ch;
63258 var sright = sleft + c.clientWidth;
63260 Roo.log('GridView.ensureVisible:' +
63262 ' c.clientHeight:' + c.clientHeight +
63263 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
63271 c.scrollTop = ctop;
63272 //Roo.log("set scrolltop to ctop DISABLE?");
63273 }else if(cbot > sbot){
63274 //Roo.log("set scrolltop to cbot-ch");
63275 c.scrollTop = cbot-ch;
63278 if(hscroll !== false){
63280 c.scrollLeft = cleft;
63281 }else if(cright > sright){
63282 c.scrollLeft = cright-c.clientWidth;
63289 updateColumns : function(){
63290 this.grid.stopEditing();
63291 var cm = this.grid.colModel, colIds = this.getColumnIds();
63292 //var totalWidth = cm.getTotalWidth();
63294 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63295 //if(cm.isHidden(i)) continue;
63296 var w = cm.getColumnWidth(i);
63297 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63298 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63300 this.updateSplitters();
63303 generateRules : function(cm){
63304 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
63305 Roo.util.CSS.removeStyleSheet(rulesId);
63306 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63307 var cid = cm.getColumnId(i);
63309 if(cm.config[i].align){
63310 align = 'text-align:'+cm.config[i].align+';';
63313 if(cm.isHidden(i)){
63314 hidden = 'display:none;';
63316 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
63318 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
63319 this.hdSelector, cid, " {\n", align, width, "}\n",
63320 this.tdSelector, cid, " {\n",hidden,"\n}\n",
63321 this.splitSelector, cid, " {\n", hidden , "\n}\n");
63323 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
63326 updateSplitters : function(){
63327 var cm = this.cm, s = this.getSplitters();
63328 if(s){ // splitters not created yet
63329 var pos = 0, locked = true;
63330 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63331 if(cm.isHidden(i)) {
63334 var w = cm.getColumnWidth(i); // make sure it's a number
63335 if(!cm.isLocked(i) && locked){
63340 s[i].style.left = (pos-this.splitOffset) + "px";
63345 handleHiddenChange : function(colModel, colIndex, hidden){
63347 this.hideColumn(colIndex);
63349 this.unhideColumn(colIndex);
63353 hideColumn : function(colIndex){
63354 var cid = this.getColumnId(colIndex);
63355 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
63356 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
63358 this.updateHeaders();
63360 this.updateSplitters();
63364 unhideColumn : function(colIndex){
63365 var cid = this.getColumnId(colIndex);
63366 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
63367 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
63370 this.updateHeaders();
63372 this.updateSplitters();
63376 insertRows : function(dm, firstRow, lastRow, isUpdate){
63377 if(firstRow == 0 && lastRow == dm.getCount()-1){
63381 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
63383 var s = this.getScrollState();
63384 var markup = this.renderRows(firstRow, lastRow);
63385 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
63386 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
63387 this.restoreScroll(s);
63389 this.fireEvent("rowsinserted", this, firstRow, lastRow);
63390 this.syncRowHeights(firstRow, lastRow);
63391 this.stripeRows(firstRow);
63397 bufferRows : function(markup, target, index){
63398 var before = null, trows = target.rows, tbody = target.tBodies[0];
63399 if(index < trows.length){
63400 before = trows[index];
63402 var b = document.createElement("div");
63403 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
63404 var rows = b.firstChild.rows;
63405 for(var i = 0, len = rows.length; i < len; i++){
63407 tbody.insertBefore(rows[0], before);
63409 tbody.appendChild(rows[0]);
63416 deleteRows : function(dm, firstRow, lastRow){
63417 if(dm.getRowCount()<1){
63418 this.fireEvent("beforerefresh", this);
63419 this.mainBody.update("");
63420 this.lockedBody.update("");
63421 this.fireEvent("refresh", this);
63423 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
63424 var bt = this.getBodyTable();
63425 var tbody = bt.firstChild;
63426 var rows = bt.rows;
63427 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
63428 tbody.removeChild(rows[firstRow]);
63430 this.stripeRows(firstRow);
63431 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
63435 updateRows : function(dataSource, firstRow, lastRow){
63436 var s = this.getScrollState();
63438 this.restoreScroll(s);
63441 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
63445 this.updateHeaderSortState();
63448 getScrollState : function(){
63450 var sb = this.scroller.dom;
63451 return {left: sb.scrollLeft, top: sb.scrollTop};
63454 stripeRows : function(startRow){
63455 if(!this.grid.stripeRows || this.ds.getCount() < 1){
63458 startRow = startRow || 0;
63459 var rows = this.getBodyTable().rows;
63460 var lrows = this.getLockedTable().rows;
63461 var cls = ' x-grid-row-alt ';
63462 for(var i = startRow, len = rows.length; i < len; i++){
63463 var row = rows[i], lrow = lrows[i];
63464 var isAlt = ((i+1) % 2 == 0);
63465 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
63466 if(isAlt == hasAlt){
63470 row.className += " x-grid-row-alt";
63472 row.className = row.className.replace("x-grid-row-alt", "");
63475 lrow.className = row.className;
63480 restoreScroll : function(state){
63481 //Roo.log('GridView.restoreScroll');
63482 var sb = this.scroller.dom;
63483 sb.scrollLeft = state.left;
63484 sb.scrollTop = state.top;
63488 syncScroll : function(){
63489 //Roo.log('GridView.syncScroll');
63490 var sb = this.scroller.dom;
63491 var sh = this.mainHd.dom;
63492 var bs = this.mainBody.dom;
63493 var lv = this.lockedBody.dom;
63494 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
63495 lv.scrollTop = bs.scrollTop = sb.scrollTop;
63498 handleScroll : function(e){
63500 var sb = this.scroller.dom;
63501 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
63505 handleWheel : function(e){
63506 var d = e.getWheelDelta();
63507 this.scroller.dom.scrollTop -= d*22;
63508 // set this here to prevent jumpy scrolling on large tables
63509 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
63513 renderRows : function(startRow, endRow){
63514 // pull in all the crap needed to render rows
63515 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
63516 var colCount = cm.getColumnCount();
63518 if(ds.getCount() < 1){
63522 // build a map for all the columns
63524 for(var i = 0; i < colCount; i++){
63525 var name = cm.getDataIndex(i);
63527 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
63528 renderer : cm.getRenderer(i),
63529 id : cm.getColumnId(i),
63530 locked : cm.isLocked(i),
63531 has_editor : cm.isCellEditable(i)
63535 startRow = startRow || 0;
63536 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
63538 // records to render
63539 var rs = ds.getRange(startRow, endRow);
63541 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
63544 // As much as I hate to duplicate code, this was branched because FireFox really hates
63545 // [].join("") on strings. The performance difference was substantial enough to
63546 // branch this function
63547 doRender : Roo.isGecko ?
63548 function(cs, rs, ds, startRow, colCount, stripe){
63549 var ts = this.templates, ct = ts.cell, rt = ts.row;
63551 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63553 var hasListener = this.grid.hasListener('rowclass');
63555 for(var j = 0, len = rs.length; j < len; j++){
63556 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
63557 for(var i = 0; i < colCount; i++){
63559 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63561 p.css = p.attr = "";
63562 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63563 if(p.value == undefined || p.value === "") {
63564 p.value = " ";
63567 p.css += ' x-grid-editable-cell';
63569 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
63570 p.css += ' x-grid-dirty-cell';
63572 var markup = ct.apply(p);
63580 if(stripe && ((rowIndex+1) % 2 == 0)){
63581 alt.push("x-grid-row-alt")
63584 alt.push( " x-grid-dirty-row");
63587 if(this.getRowClass){
63588 alt.push(this.getRowClass(r, rowIndex));
63594 rowIndex : rowIndex,
63597 this.grid.fireEvent('rowclass', this, rowcfg);
63598 alt.push(rowcfg.rowClass);
63600 rp.alt = alt.join(" ");
63601 lbuf+= rt.apply(rp);
63603 buf+= rt.apply(rp);
63605 return [lbuf, buf];
63607 function(cs, rs, ds, startRow, colCount, stripe){
63608 var ts = this.templates, ct = ts.cell, rt = ts.row;
63610 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63611 var hasListener = this.grid.hasListener('rowclass');
63614 for(var j = 0, len = rs.length; j < len; j++){
63615 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
63616 for(var i = 0; i < colCount; i++){
63618 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63620 p.css = p.attr = "";
63621 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63622 if(p.value == undefined || p.value === "") {
63623 p.value = " ";
63627 p.css += ' x-grid-editable-cell';
63629 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
63630 p.css += ' x-grid-dirty-cell'
63633 var markup = ct.apply(p);
63635 cb[cb.length] = markup;
63637 lcb[lcb.length] = markup;
63641 if(stripe && ((rowIndex+1) % 2 == 0)){
63642 alt.push( "x-grid-row-alt");
63645 alt.push(" x-grid-dirty-row");
63648 if(this.getRowClass){
63649 alt.push( this.getRowClass(r, rowIndex));
63655 rowIndex : rowIndex,
63658 this.grid.fireEvent('rowclass', this, rowcfg);
63659 alt.push(rowcfg.rowClass);
63662 rp.alt = alt.join(" ");
63663 rp.cells = lcb.join("");
63664 lbuf[lbuf.length] = rt.apply(rp);
63665 rp.cells = cb.join("");
63666 buf[buf.length] = rt.apply(rp);
63668 return [lbuf.join(""), buf.join("")];
63671 renderBody : function(){
63672 var markup = this.renderRows();
63673 var bt = this.templates.body;
63674 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
63678 * Refreshes the grid
63679 * @param {Boolean} headersToo
63681 refresh : function(headersToo){
63682 this.fireEvent("beforerefresh", this);
63683 this.grid.stopEditing();
63684 var result = this.renderBody();
63685 this.lockedBody.update(result[0]);
63686 this.mainBody.update(result[1]);
63687 if(headersToo === true){
63688 this.updateHeaders();
63689 this.updateColumns();
63690 this.updateSplitters();
63691 this.updateHeaderSortState();
63693 this.syncRowHeights();
63695 this.fireEvent("refresh", this);
63698 handleColumnMove : function(cm, oldIndex, newIndex){
63699 this.indexMap = null;
63700 var s = this.getScrollState();
63701 this.refresh(true);
63702 this.restoreScroll(s);
63703 this.afterMove(newIndex);
63706 afterMove : function(colIndex){
63707 if(this.enableMoveAnim && Roo.enableFx){
63708 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
63710 // if multisort - fix sortOrder, and reload..
63711 if (this.grid.dataSource.multiSort) {
63712 // the we can call sort again..
63713 var dm = this.grid.dataSource;
63714 var cm = this.grid.colModel;
63716 for(var i = 0; i < cm.config.length; i++ ) {
63718 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
63719 continue; // dont' bother, it's not in sort list or being set.
63722 so.push(cm.config[i].dataIndex);
63725 dm.load(dm.lastOptions);
63732 updateCell : function(dm, rowIndex, dataIndex){
63733 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
63734 if(typeof colIndex == "undefined"){ // not present in grid
63737 var cm = this.grid.colModel;
63738 var cell = this.getCell(rowIndex, colIndex);
63739 var cellText = this.getCellText(rowIndex, colIndex);
63742 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
63743 id : cm.getColumnId(colIndex),
63744 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
63746 var renderer = cm.getRenderer(colIndex);
63747 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
63748 if(typeof val == "undefined" || val === "") {
63751 cellText.innerHTML = val;
63752 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
63753 this.syncRowHeights(rowIndex, rowIndex);
63756 calcColumnWidth : function(colIndex, maxRowsToMeasure){
63758 if(this.grid.autoSizeHeaders){
63759 var h = this.getHeaderCellMeasure(colIndex);
63760 maxWidth = Math.max(maxWidth, h.scrollWidth);
63763 if(this.cm.isLocked(colIndex)){
63764 tb = this.getLockedTable();
63767 tb = this.getBodyTable();
63768 index = colIndex - this.cm.getLockedCount();
63771 var rows = tb.rows;
63772 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
63773 for(var i = 0; i < stopIndex; i++){
63774 var cell = rows[i].childNodes[index].firstChild;
63775 maxWidth = Math.max(maxWidth, cell.scrollWidth);
63778 return maxWidth + /*margin for error in IE*/ 5;
63781 * Autofit a column to its content.
63782 * @param {Number} colIndex
63783 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
63785 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
63786 if(this.cm.isHidden(colIndex)){
63787 return; // can't calc a hidden column
63790 var cid = this.cm.getColumnId(colIndex);
63791 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
63792 if(this.grid.autoSizeHeaders){
63793 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
63796 var newWidth = this.calcColumnWidth(colIndex);
63797 this.cm.setColumnWidth(colIndex,
63798 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
63799 if(!suppressEvent){
63800 this.grid.fireEvent("columnresize", colIndex, newWidth);
63805 * Autofits all columns to their content and then expands to fit any extra space in the grid
63807 autoSizeColumns : function(){
63808 var cm = this.grid.colModel;
63809 var colCount = cm.getColumnCount();
63810 for(var i = 0; i < colCount; i++){
63811 this.autoSizeColumn(i, true, true);
63813 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
63816 this.updateColumns();
63822 * Autofits all columns to the grid's width proportionate with their current size
63823 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
63825 fitColumns : function(reserveScrollSpace){
63826 var cm = this.grid.colModel;
63827 var colCount = cm.getColumnCount();
63831 for (i = 0; i < colCount; i++){
63832 if(!cm.isHidden(i) && !cm.isFixed(i)){
63833 w = cm.getColumnWidth(i);
63839 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
63840 if(reserveScrollSpace){
63843 var frac = (avail - cm.getTotalWidth())/width;
63844 while (cols.length){
63847 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
63849 this.updateColumns();
63853 onRowSelect : function(rowIndex){
63854 var row = this.getRowComposite(rowIndex);
63855 row.addClass("x-grid-row-selected");
63858 onRowDeselect : function(rowIndex){
63859 var row = this.getRowComposite(rowIndex);
63860 row.removeClass("x-grid-row-selected");
63863 onCellSelect : function(row, col){
63864 var cell = this.getCell(row, col);
63866 Roo.fly(cell).addClass("x-grid-cell-selected");
63870 onCellDeselect : function(row, col){
63871 var cell = this.getCell(row, col);
63873 Roo.fly(cell).removeClass("x-grid-cell-selected");
63877 updateHeaderSortState : function(){
63879 // sort state can be single { field: xxx, direction : yyy}
63880 // or { xxx=>ASC , yyy : DESC ..... }
63883 if (!this.ds.multiSort) {
63884 var state = this.ds.getSortState();
63888 mstate[state.field] = state.direction;
63889 // FIXME... - this is not used here.. but might be elsewhere..
63890 this.sortState = state;
63893 mstate = this.ds.sortToggle;
63895 //remove existing sort classes..
63897 var sc = this.sortClasses;
63898 var hds = this.el.select(this.headerSelector).removeClass(sc);
63900 for(var f in mstate) {
63902 var sortColumn = this.cm.findColumnIndex(f);
63904 if(sortColumn != -1){
63905 var sortDir = mstate[f];
63906 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
63915 handleHeaderClick : function(g, index,e){
63917 Roo.log("header click");
63920 // touch events on header are handled by context
63921 this.handleHdCtx(g,index,e);
63926 if(this.headersDisabled){
63929 var dm = g.dataSource, cm = g.colModel;
63930 if(!cm.isSortable(index)){
63935 if (dm.multiSort) {
63936 // update the sortOrder
63938 for(var i = 0; i < cm.config.length; i++ ) {
63940 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
63941 continue; // dont' bother, it's not in sort list or being set.
63944 so.push(cm.config[i].dataIndex);
63950 dm.sort(cm.getDataIndex(index));
63954 destroy : function(){
63956 this.colMenu.removeAll();
63957 Roo.menu.MenuMgr.unregister(this.colMenu);
63958 this.colMenu.getEl().remove();
63959 delete this.colMenu;
63962 this.hmenu.removeAll();
63963 Roo.menu.MenuMgr.unregister(this.hmenu);
63964 this.hmenu.getEl().remove();
63967 if(this.grid.enableColumnMove){
63968 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63970 for(var dd in dds){
63971 if(!dds[dd].config.isTarget && dds[dd].dragElId){
63972 var elid = dds[dd].dragElId;
63974 Roo.get(elid).remove();
63975 } else if(dds[dd].config.isTarget){
63976 dds[dd].proxyTop.remove();
63977 dds[dd].proxyBottom.remove();
63980 if(Roo.dd.DDM.locationCache[dd]){
63981 delete Roo.dd.DDM.locationCache[dd];
63984 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63987 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
63988 this.bind(null, null);
63989 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
63992 handleLockChange : function(){
63993 this.refresh(true);
63996 onDenyColumnLock : function(){
64000 onDenyColumnHide : function(){
64004 handleHdMenuClick : function(item){
64005 var index = this.hdCtxIndex;
64006 var cm = this.cm, ds = this.ds;
64009 ds.sort(cm.getDataIndex(index), "ASC");
64012 ds.sort(cm.getDataIndex(index), "DESC");
64015 var lc = cm.getLockedCount();
64016 if(cm.getColumnCount(true) <= lc+1){
64017 this.onDenyColumnLock();
64021 cm.setLocked(index, true, true);
64022 cm.moveColumn(index, lc);
64023 this.grid.fireEvent("columnmove", index, lc);
64025 cm.setLocked(index, true);
64029 var lc = cm.getLockedCount();
64030 if((lc-1) != index){
64031 cm.setLocked(index, false, true);
64032 cm.moveColumn(index, lc-1);
64033 this.grid.fireEvent("columnmove", index, lc-1);
64035 cm.setLocked(index, false);
64038 case 'wider': // used to expand cols on touch..
64040 var cw = cm.getColumnWidth(index);
64041 cw += (item.id == 'wider' ? 1 : -1) * 50;
64042 cw = Math.max(0, cw);
64043 cw = Math.min(cw,4000);
64044 cm.setColumnWidth(index, cw);
64048 index = cm.getIndexById(item.id.substr(4));
64050 if(item.checked && cm.getColumnCount(true) <= 1){
64051 this.onDenyColumnHide();
64054 cm.setHidden(index, item.checked);
64060 beforeColMenuShow : function(){
64061 var cm = this.cm, colCount = cm.getColumnCount();
64062 this.colMenu.removeAll();
64065 for(var i = 0; i < colCount; i++){
64067 id: "col-"+cm.getColumnId(i),
64068 text: cm.getColumnHeader(i),
64069 checked: !cm.isHidden(i),
64074 if (this.grid.sortColMenu) {
64075 items.sort(function(a,b) {
64076 if (a.text == b.text) {
64079 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
64083 for(var i = 0; i < colCount; i++){
64084 this.colMenu.add(new Roo.menu.CheckItem(items[i]));
64088 handleHdCtx : function(g, index, e){
64090 var hd = this.getHeaderCell(index);
64091 this.hdCtxIndex = index;
64092 var ms = this.hmenu.items, cm = this.cm;
64093 ms.get("asc").setDisabled(!cm.isSortable(index));
64094 ms.get("desc").setDisabled(!cm.isSortable(index));
64095 if(this.grid.enableColLock !== false){
64096 ms.get("lock").setDisabled(cm.isLocked(index));
64097 ms.get("unlock").setDisabled(!cm.isLocked(index));
64099 this.hmenu.show(hd, "tl-bl");
64102 handleHdOver : function(e){
64103 var hd = this.findHeaderCell(e.getTarget());
64104 if(hd && !this.headersDisabled){
64105 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
64106 this.fly(hd).addClass("x-grid-hd-over");
64111 handleHdOut : function(e){
64112 var hd = this.findHeaderCell(e.getTarget());
64114 this.fly(hd).removeClass("x-grid-hd-over");
64118 handleSplitDblClick : function(e, t){
64119 var i = this.getCellIndex(t);
64120 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
64121 this.autoSizeColumn(i, true);
64126 render : function(){
64129 var colCount = cm.getColumnCount();
64131 if(this.grid.monitorWindowResize === true){
64132 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
64134 var header = this.renderHeaders();
64135 var body = this.templates.body.apply({rows:""});
64136 var html = this.templates.master.apply({
64139 lockedHeader: header[0],
64143 //this.updateColumns();
64145 this.grid.getGridEl().dom.innerHTML = html;
64147 this.initElements();
64149 // a kludge to fix the random scolling effect in webkit
64150 this.el.on("scroll", function() {
64151 this.el.dom.scrollTop=0; // hopefully not recursive..
64154 this.scroller.on("scroll", this.handleScroll, this);
64155 this.lockedBody.on("mousewheel", this.handleWheel, this);
64156 this.mainBody.on("mousewheel", this.handleWheel, this);
64158 this.mainHd.on("mouseover", this.handleHdOver, this);
64159 this.mainHd.on("mouseout", this.handleHdOut, this);
64160 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
64161 {delegate: "."+this.splitClass});
64163 this.lockedHd.on("mouseover", this.handleHdOver, this);
64164 this.lockedHd.on("mouseout", this.handleHdOut, this);
64165 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
64166 {delegate: "."+this.splitClass});
64168 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
64169 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64172 this.updateSplitters();
64174 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
64175 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64176 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64179 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
64180 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
64182 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
64183 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
64185 if(this.grid.enableColLock !== false){
64186 this.hmenu.add('-',
64187 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
64188 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
64192 this.hmenu.add('-',
64193 {id:"wider", text: this.columnsWiderText},
64194 {id:"narrow", text: this.columnsNarrowText }
64200 if(this.grid.enableColumnHide !== false){
64202 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
64203 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
64204 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
64206 this.hmenu.add('-',
64207 {id:"columns", text: this.columnsText, menu: this.colMenu}
64210 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
64212 this.grid.on("headercontextmenu", this.handleHdCtx, this);
64215 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
64216 this.dd = new Roo.grid.GridDragZone(this.grid, {
64217 ddGroup : this.grid.ddGroup || 'GridDD'
64223 for(var i = 0; i < colCount; i++){
64224 if(cm.isHidden(i)){
64225 this.hideColumn(i);
64227 if(cm.config[i].align){
64228 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
64229 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
64233 this.updateHeaderSortState();
64235 this.beforeInitialResize();
64238 // two part rendering gives faster view to the user
64239 this.renderPhase2.defer(1, this);
64242 renderPhase2 : function(){
64243 // render the rows now
64245 if(this.grid.autoSizeColumns){
64246 this.autoSizeColumns();
64250 beforeInitialResize : function(){
64254 onColumnSplitterMoved : function(i, w){
64255 this.userResized = true;
64256 var cm = this.grid.colModel;
64257 cm.setColumnWidth(i, w, true);
64258 var cid = cm.getColumnId(i);
64259 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
64260 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
64261 this.updateSplitters();
64263 this.grid.fireEvent("columnresize", i, w);
64266 syncRowHeights : function(startIndex, endIndex){
64267 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
64268 startIndex = startIndex || 0;
64269 var mrows = this.getBodyTable().rows;
64270 var lrows = this.getLockedTable().rows;
64271 var len = mrows.length-1;
64272 endIndex = Math.min(endIndex || len, len);
64273 for(var i = startIndex; i <= endIndex; i++){
64274 var m = mrows[i], l = lrows[i];
64275 var h = Math.max(m.offsetHeight, l.offsetHeight);
64276 m.style.height = l.style.height = h + "px";
64281 layout : function(initialRender, is2ndPass)
64284 var auto = g.autoHeight;
64285 var scrollOffset = 16;
64286 var c = g.getGridEl(), cm = this.cm,
64287 expandCol = g.autoExpandColumn,
64289 //c.beginMeasure();
64291 if(!c.dom.offsetWidth){ // display:none?
64293 this.lockedWrap.show();
64294 this.mainWrap.show();
64299 var hasLock = this.cm.isLocked(0);
64301 var tbh = this.headerPanel.getHeight();
64302 var bbh = this.footerPanel.getHeight();
64305 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
64306 var newHeight = ch + c.getBorderWidth("tb");
64308 newHeight = Math.min(g.maxHeight, newHeight);
64310 c.setHeight(newHeight);
64314 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
64317 var s = this.scroller;
64319 var csize = c.getSize(true);
64321 this.el.setSize(csize.width, csize.height);
64323 this.headerPanel.setWidth(csize.width);
64324 this.footerPanel.setWidth(csize.width);
64326 var hdHeight = this.mainHd.getHeight();
64327 var vw = csize.width;
64328 var vh = csize.height - (tbh + bbh);
64332 var bt = this.getBodyTable();
64334 if(cm.getLockedCount() == cm.config.length){
64335 bt = this.getLockedTable();
64338 var ltWidth = hasLock ?
64339 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
64341 var scrollHeight = bt.offsetHeight;
64342 var scrollWidth = ltWidth + bt.offsetWidth;
64343 var vscroll = false, hscroll = false;
64345 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
64347 var lw = this.lockedWrap, mw = this.mainWrap;
64348 var lb = this.lockedBody, mb = this.mainBody;
64350 setTimeout(function(){
64351 var t = s.dom.offsetTop;
64352 var w = s.dom.clientWidth,
64353 h = s.dom.clientHeight;
64356 lw.setSize(ltWidth, h);
64358 mw.setLeftTop(ltWidth, t);
64359 mw.setSize(w-ltWidth, h);
64361 lb.setHeight(h-hdHeight);
64362 mb.setHeight(h-hdHeight);
64364 if(is2ndPass !== true && !gv.userResized && expandCol){
64365 // high speed resize without full column calculation
64367 var ci = cm.getIndexById(expandCol);
64369 ci = cm.findColumnIndex(expandCol);
64371 ci = Math.max(0, ci); // make sure it's got at least the first col.
64372 var expandId = cm.getColumnId(ci);
64373 var tw = cm.getTotalWidth(false);
64374 var currentWidth = cm.getColumnWidth(ci);
64375 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
64376 if(currentWidth != cw){
64377 cm.setColumnWidth(ci, cw, true);
64378 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64379 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64380 gv.updateSplitters();
64381 gv.layout(false, true);
64393 onWindowResize : function(){
64394 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
64400 appendFooter : function(parentEl){
64404 sortAscText : "Sort Ascending",
64405 sortDescText : "Sort Descending",
64406 lockText : "Lock Column",
64407 unlockText : "Unlock Column",
64408 columnsText : "Columns",
64410 columnsWiderText : "Wider",
64411 columnsNarrowText : "Thinner"
64415 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
64416 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
64417 this.proxy.el.addClass('x-grid3-col-dd');
64420 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
64421 handleMouseDown : function(e){
64425 callHandleMouseDown : function(e){
64426 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
64431 * Ext JS Library 1.1.1
64432 * Copyright(c) 2006-2007, Ext JS, LLC.
64434 * Originally Released Under LGPL - original licence link has changed is not relivant.
64437 * <script type="text/javascript">
64440 * @extends Roo.dd.DDProxy
64441 * @class Roo.grid.SplitDragZone
64442 * Support for Column Header resizing
64444 * @param {Object} config
64447 // This is a support class used internally by the Grid components
64448 Roo.grid.SplitDragZone = function(grid, hd, hd2){
64450 this.view = grid.getView();
64451 this.proxy = this.view.resizeProxy;
64452 Roo.grid.SplitDragZone.superclass.constructor.call(
64455 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
64457 dragElId : Roo.id(this.proxy.dom),
64462 this.setHandleElId(Roo.id(hd));
64463 if (hd2 !== false) {
64464 this.setOuterHandleElId(Roo.id(hd2));
64467 this.scroll = false;
64469 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
64470 fly: Roo.Element.fly,
64472 b4StartDrag : function(x, y){
64473 this.view.headersDisabled = true;
64474 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
64475 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
64477 this.proxy.setHeight(h);
64479 // for old system colWidth really stored the actual width?
64480 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
64481 // which in reality did not work.. - it worked only for fixed sizes
64482 // for resizable we need to use actual sizes.
64483 var w = this.cm.getColumnWidth(this.cellIndex);
64484 if (!this.view.mainWrap) {
64486 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
64491 // this was w-this.grid.minColumnWidth;
64492 // doesnt really make sense? - w = thie curren width or the rendered one?
64493 var minw = Math.max(w-this.grid.minColumnWidth, 0);
64494 this.resetConstraints();
64495 this.setXConstraint(minw, 1000);
64496 this.setYConstraint(0, 0);
64497 this.minX = x - minw;
64498 this.maxX = x + 1000;
64500 if (!this.view.mainWrap) { // this is Bootstrap code..
64501 this.getDragEl().style.display='block';
64504 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
64508 handleMouseDown : function(e){
64509 ev = Roo.EventObject.setEvent(e);
64510 var t = this.fly(ev.getTarget());
64511 if(t.hasClass("x-grid-split")){
64512 this.cellIndex = this.view.getCellIndex(t.dom);
64513 this.split = t.dom;
64514 this.cm = this.grid.colModel;
64515 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
64516 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
64521 endDrag : function(e){
64522 this.view.headersDisabled = false;
64523 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
64524 var diff = endX - this.startPos;
64526 var w = this.cm.getColumnWidth(this.cellIndex);
64527 if (!this.view.mainWrap) {
64530 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
64533 autoOffset : function(){
64534 this.setDelta(0,0);
64538 * Ext JS Library 1.1.1
64539 * Copyright(c) 2006-2007, Ext JS, LLC.
64541 * Originally Released Under LGPL - original licence link has changed is not relivant.
64544 * <script type="text/javascript">
64548 // This is a support class used internally by the Grid components
64549 Roo.grid.GridDragZone = function(grid, config){
64550 this.view = grid.getView();
64551 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
64552 if(this.view.lockedBody){
64553 this.setHandleElId(Roo.id(this.view.mainBody.dom));
64554 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
64556 this.scroll = false;
64558 this.ddel = document.createElement('div');
64559 this.ddel.className = 'x-grid-dd-wrap';
64562 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
64563 ddGroup : "GridDD",
64565 getDragData : function(e){
64566 var t = Roo.lib.Event.getTarget(e);
64567 var rowIndex = this.view.findRowIndex(t);
64568 var sm = this.grid.selModel;
64570 //Roo.log(rowIndex);
64572 if (sm.getSelectedCell) {
64573 // cell selection..
64574 if (!sm.getSelectedCell()) {
64577 if (rowIndex != sm.getSelectedCell()[0]) {
64582 if (sm.getSelections && sm.getSelections().length < 1) {
64587 // before it used to all dragging of unseleted... - now we dont do that.
64588 if(rowIndex !== false){
64593 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
64595 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
64598 if (e.hasModifier()){
64599 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
64602 Roo.log("getDragData");
64607 rowIndex: rowIndex,
64608 selections: sm.getSelections ? sm.getSelections() : (
64609 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
64616 onInitDrag : function(e){
64617 var data = this.dragData;
64618 this.ddel.innerHTML = this.grid.getDragDropText();
64619 this.proxy.update(this.ddel);
64620 // fire start drag?
64623 afterRepair : function(){
64624 this.dragging = false;
64627 getRepairXY : function(e, data){
64631 onEndDrag : function(data, e){
64635 onValidDrop : function(dd, e, id){
64640 beforeInvalidDrop : function(e, id){
64645 * Ext JS Library 1.1.1
64646 * Copyright(c) 2006-2007, Ext JS, LLC.
64648 * Originally Released Under LGPL - original licence link has changed is not relivant.
64651 * <script type="text/javascript">
64656 * @class Roo.grid.ColumnModel
64657 * @extends Roo.util.Observable
64658 * This is the default implementation of a ColumnModel used by the Grid. It defines
64659 * the columns in the grid.
64662 var colModel = new Roo.grid.ColumnModel([
64663 {header: "Ticker", width: 60, sortable: true, locked: true},
64664 {header: "Company Name", width: 150, sortable: true},
64665 {header: "Market Cap.", width: 100, sortable: true},
64666 {header: "$ Sales", width: 100, sortable: true, renderer: money},
64667 {header: "Employees", width: 100, sortable: true, resizable: false}
64672 * The config options listed for this class are options which may appear in each
64673 * individual column definition.
64674 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
64676 * @param {Object} config An Array of column config objects. See this class's
64677 * config objects for details.
64679 Roo.grid.ColumnModel = function(config){
64681 * The config passed into the constructor
64683 this.config = []; //config;
64686 // if no id, create one
64687 // if the column does not have a dataIndex mapping,
64688 // map it to the order it is in the config
64689 for(var i = 0, len = config.length; i < len; i++){
64690 this.addColumn(config[i]);
64695 * The width of columns which have no width specified (defaults to 100)
64698 this.defaultWidth = 100;
64701 * Default sortable of columns which have no sortable specified (defaults to false)
64704 this.defaultSortable = false;
64708 * @event widthchange
64709 * Fires when the width of a column changes.
64710 * @param {ColumnModel} this
64711 * @param {Number} columnIndex The column index
64712 * @param {Number} newWidth The new width
64714 "widthchange": true,
64716 * @event headerchange
64717 * Fires when the text of a header changes.
64718 * @param {ColumnModel} this
64719 * @param {Number} columnIndex The column index
64720 * @param {Number} newText The new header text
64722 "headerchange": true,
64724 * @event hiddenchange
64725 * Fires when a column is hidden or "unhidden".
64726 * @param {ColumnModel} this
64727 * @param {Number} columnIndex The column index
64728 * @param {Boolean} hidden true if hidden, false otherwise
64730 "hiddenchange": true,
64732 * @event columnmoved
64733 * Fires when a column is moved.
64734 * @param {ColumnModel} this
64735 * @param {Number} oldIndex
64736 * @param {Number} newIndex
64738 "columnmoved" : true,
64740 * @event columlockchange
64741 * Fires when a column's locked state is changed
64742 * @param {ColumnModel} this
64743 * @param {Number} colIndex
64744 * @param {Boolean} locked true if locked
64746 "columnlockchange" : true
64748 Roo.grid.ColumnModel.superclass.constructor.call(this);
64750 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
64752 * @cfg {String} header [required] The header text to display in the Grid view.
64755 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
64758 * @cfg {String} smHeader Header at Bootsrap Small width
64761 * @cfg {String} mdHeader Header at Bootsrap Medium width
64764 * @cfg {String} lgHeader Header at Bootsrap Large width
64767 * @cfg {String} xlHeader Header at Bootsrap extra Large width
64770 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
64771 * {@link Roo.data.Record} definition from which to draw the column's value. If not
64772 * specified, the column's index is used as an index into the Record's data Array.
64775 * @cfg {Number} width The initial width in pixels of the column. Using this
64776 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
64779 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
64780 * Defaults to the value of the {@link #defaultSortable} property.
64781 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
64784 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
64787 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
64790 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
64793 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
64796 * @cfg {Function} renderer A function used to generate HTML markup for a cell
64797 * given the cell's data value. See {@link #setRenderer}. If not specified, the
64798 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
64799 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
64802 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
64805 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
64808 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
64811 * @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)
64814 * @cfg {String} tooltip mouse over tooltip text
64817 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
64820 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
64823 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
64826 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
64829 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
64832 * Returns the id of the column at the specified index.
64833 * @param {Number} index The column index
64834 * @return {String} the id
64836 getColumnId : function(index){
64837 return this.config[index].id;
64841 * Returns the column for a specified id.
64842 * @param {String} id The column id
64843 * @return {Object} the column
64845 getColumnById : function(id){
64846 return this.lookup[id];
64851 * Returns the column Object for a specified dataIndex.
64852 * @param {String} dataIndex The column dataIndex
64853 * @return {Object|Boolean} the column or false if not found
64855 getColumnByDataIndex: function(dataIndex){
64856 var index = this.findColumnIndex(dataIndex);
64857 return index > -1 ? this.config[index] : false;
64861 * Returns the index for a specified column id.
64862 * @param {String} id The column id
64863 * @return {Number} the index, or -1 if not found
64865 getIndexById : function(id){
64866 for(var i = 0, len = this.config.length; i < len; i++){
64867 if(this.config[i].id == id){
64875 * Returns the index for a specified column dataIndex.
64876 * @param {String} dataIndex The column dataIndex
64877 * @return {Number} the index, or -1 if not found
64880 findColumnIndex : function(dataIndex){
64881 for(var i = 0, len = this.config.length; i < len; i++){
64882 if(this.config[i].dataIndex == dataIndex){
64890 moveColumn : function(oldIndex, newIndex){
64891 var c = this.config[oldIndex];
64892 this.config.splice(oldIndex, 1);
64893 this.config.splice(newIndex, 0, c);
64894 this.dataMap = null;
64895 this.fireEvent("columnmoved", this, oldIndex, newIndex);
64898 isLocked : function(colIndex){
64899 return this.config[colIndex].locked === true;
64902 setLocked : function(colIndex, value, suppressEvent){
64903 if(this.isLocked(colIndex) == value){
64906 this.config[colIndex].locked = value;
64907 if(!suppressEvent){
64908 this.fireEvent("columnlockchange", this, colIndex, value);
64912 getTotalLockedWidth : function(){
64913 var totalWidth = 0;
64914 for(var i = 0; i < this.config.length; i++){
64915 if(this.isLocked(i) && !this.isHidden(i)){
64916 this.totalWidth += this.getColumnWidth(i);
64922 getLockedCount : function(){
64923 for(var i = 0, len = this.config.length; i < len; i++){
64924 if(!this.isLocked(i)){
64929 return this.config.length;
64933 * Returns the number of columns.
64936 getColumnCount : function(visibleOnly){
64937 if(visibleOnly === true){
64939 for(var i = 0, len = this.config.length; i < len; i++){
64940 if(!this.isHidden(i)){
64946 return this.config.length;
64950 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
64951 * @param {Function} fn
64952 * @param {Object} scope (optional)
64953 * @return {Array} result
64955 getColumnsBy : function(fn, scope){
64957 for(var i = 0, len = this.config.length; i < len; i++){
64958 var c = this.config[i];
64959 if(fn.call(scope||this, c, i) === true){
64967 * Returns true if the specified column is sortable.
64968 * @param {Number} col The column index
64969 * @return {Boolean}
64971 isSortable : function(col){
64972 if(typeof this.config[col].sortable == "undefined"){
64973 return this.defaultSortable;
64975 return this.config[col].sortable;
64979 * Returns the rendering (formatting) function defined for the column.
64980 * @param {Number} col The column index.
64981 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
64983 getRenderer : function(col){
64984 if(!this.config[col].renderer){
64985 return Roo.grid.ColumnModel.defaultRenderer;
64987 return this.config[col].renderer;
64991 * Sets the rendering (formatting) function for a column.
64992 * @param {Number} col The column index
64993 * @param {Function} fn The function to use to process the cell's raw data
64994 * to return HTML markup for the grid view. The render function is called with
64995 * the following parameters:<ul>
64996 * <li>Data value.</li>
64997 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
64998 * <li>css A CSS style string to apply to the table cell.</li>
64999 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
65000 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
65001 * <li>Row index</li>
65002 * <li>Column index</li>
65003 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
65005 setRenderer : function(col, fn){
65006 this.config[col].renderer = fn;
65010 * Returns the width for the specified column.
65011 * @param {Number} col The column index
65012 * @param (optional) {String} gridSize bootstrap width size.
65015 getColumnWidth : function(col, gridSize)
65017 var cfg = this.config[col];
65019 if (typeof(gridSize) == 'undefined') {
65020 return cfg.width * 1 || this.defaultWidth;
65022 if (gridSize === false) { // if we set it..
65023 return cfg.width || false;
65025 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
65027 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
65028 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
65031 return cfg[ sizes[i] ];
65038 * Sets the width for a column.
65039 * @param {Number} col The column index
65040 * @param {Number} width The new width
65042 setColumnWidth : function(col, width, suppressEvent){
65043 this.config[col].width = width;
65044 this.totalWidth = null;
65045 if(!suppressEvent){
65046 this.fireEvent("widthchange", this, col, width);
65051 * Returns the total width of all columns.
65052 * @param {Boolean} includeHidden True to include hidden column widths
65055 getTotalWidth : function(includeHidden){
65056 if(!this.totalWidth){
65057 this.totalWidth = 0;
65058 for(var i = 0, len = this.config.length; i < len; i++){
65059 if(includeHidden || !this.isHidden(i)){
65060 this.totalWidth += this.getColumnWidth(i);
65064 return this.totalWidth;
65068 * Returns the header for the specified column.
65069 * @param {Number} col The column index
65072 getColumnHeader : function(col){
65073 return this.config[col].header;
65077 * Sets the header for a column.
65078 * @param {Number} col The column index
65079 * @param {String} header The new header
65081 setColumnHeader : function(col, header){
65082 this.config[col].header = header;
65083 this.fireEvent("headerchange", this, col, header);
65087 * Returns the tooltip for the specified column.
65088 * @param {Number} col The column index
65091 getColumnTooltip : function(col){
65092 return this.config[col].tooltip;
65095 * Sets the tooltip for a column.
65096 * @param {Number} col The column index
65097 * @param {String} tooltip The new tooltip
65099 setColumnTooltip : function(col, tooltip){
65100 this.config[col].tooltip = tooltip;
65104 * Returns the dataIndex for the specified column.
65105 * @param {Number} col The column index
65108 getDataIndex : function(col){
65109 return this.config[col].dataIndex;
65113 * Sets the dataIndex for a column.
65114 * @param {Number} col The column index
65115 * @param {Number} dataIndex The new dataIndex
65117 setDataIndex : function(col, dataIndex){
65118 this.config[col].dataIndex = dataIndex;
65124 * Returns true if the cell is editable.
65125 * @param {Number} colIndex The column index
65126 * @param {Number} rowIndex The row index - this is nto actually used..?
65127 * @return {Boolean}
65129 isCellEditable : function(colIndex, rowIndex){
65130 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
65134 * Returns the editor defined for the cell/column.
65135 * return false or null to disable editing.
65136 * @param {Number} colIndex The column index
65137 * @param {Number} rowIndex The row index
65140 getCellEditor : function(colIndex, rowIndex){
65141 return this.config[colIndex].editor;
65145 * Sets if a column is editable.
65146 * @param {Number} col The column index
65147 * @param {Boolean} editable True if the column is editable
65149 setEditable : function(col, editable){
65150 this.config[col].editable = editable;
65155 * Returns true if the column is hidden.
65156 * @param {Number} colIndex The column index
65157 * @return {Boolean}
65159 isHidden : function(colIndex){
65160 return this.config[colIndex].hidden;
65165 * Returns true if the column width cannot be changed
65167 isFixed : function(colIndex){
65168 return this.config[colIndex].fixed;
65172 * Returns true if the column can be resized
65173 * @return {Boolean}
65175 isResizable : function(colIndex){
65176 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
65179 * Sets if a column is hidden.
65180 * @param {Number} colIndex The column index
65181 * @param {Boolean} hidden True if the column is hidden
65183 setHidden : function(colIndex, hidden){
65184 this.config[colIndex].hidden = hidden;
65185 this.totalWidth = null;
65186 this.fireEvent("hiddenchange", this, colIndex, hidden);
65190 * Sets the editor for a column.
65191 * @param {Number} col The column index
65192 * @param {Object} editor The editor object
65194 setEditor : function(col, editor){
65195 this.config[col].editor = editor;
65198 * Add a column (experimental...) - defaults to adding to the end..
65199 * @param {Object} config
65201 addColumn : function(c)
65204 var i = this.config.length;
65205 this.config[i] = c;
65207 if(typeof c.dataIndex == "undefined"){
65210 if(typeof c.renderer == "string"){
65211 c.renderer = Roo.util.Format[c.renderer];
65213 if(typeof c.id == "undefined"){
65216 if(c.editor && c.editor.xtype){
65217 c.editor = Roo.factory(c.editor, Roo.grid);
65219 if(c.editor && c.editor.isFormField){
65220 c.editor = new Roo.grid.GridEditor(c.editor);
65222 this.lookup[c.id] = c;
65227 Roo.grid.ColumnModel.defaultRenderer = function(value)
65229 if(typeof value == "object") {
65232 if(typeof value == "string" && value.length < 1){
65236 return String.format("{0}", value);
65239 // Alias for backwards compatibility
65240 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
65243 * Ext JS Library 1.1.1
65244 * Copyright(c) 2006-2007, Ext JS, LLC.
65246 * Originally Released Under LGPL - original licence link has changed is not relivant.
65249 * <script type="text/javascript">
65253 * @class Roo.grid.AbstractSelectionModel
65254 * @extends Roo.util.Observable
65256 * Abstract base class for grid SelectionModels. It provides the interface that should be
65257 * implemented by descendant classes. This class should not be directly instantiated.
65260 Roo.grid.AbstractSelectionModel = function(){
65261 this.locked = false;
65262 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
65265 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
65266 /** @ignore Called by the grid automatically. Do not call directly. */
65267 init : function(grid){
65273 * Locks the selections.
65276 this.locked = true;
65280 * Unlocks the selections.
65282 unlock : function(){
65283 this.locked = false;
65287 * Returns true if the selections are locked.
65288 * @return {Boolean}
65290 isLocked : function(){
65291 return this.locked;
65295 * Ext JS Library 1.1.1
65296 * Copyright(c) 2006-2007, Ext JS, LLC.
65298 * Originally Released Under LGPL - original licence link has changed is not relivant.
65301 * <script type="text/javascript">
65304 * @extends Roo.grid.AbstractSelectionModel
65305 * @class Roo.grid.RowSelectionModel
65306 * The default SelectionModel used by {@link Roo.grid.Grid}.
65307 * It supports multiple selections and keyboard selection/navigation.
65309 * @param {Object} config
65311 Roo.grid.RowSelectionModel = function(config){
65312 Roo.apply(this, config);
65313 this.selections = new Roo.util.MixedCollection(false, function(o){
65318 this.lastActive = false;
65322 * @event selectionchange
65323 * Fires when the selection changes
65324 * @param {SelectionModel} this
65326 "selectionchange" : true,
65328 * @event afterselectionchange
65329 * Fires after the selection changes (eg. by key press or clicking)
65330 * @param {SelectionModel} this
65332 "afterselectionchange" : true,
65334 * @event beforerowselect
65335 * Fires when a row is selected being selected, return false to cancel.
65336 * @param {SelectionModel} this
65337 * @param {Number} rowIndex The selected index
65338 * @param {Boolean} keepExisting False if other selections will be cleared
65340 "beforerowselect" : true,
65343 * Fires when a row is selected.
65344 * @param {SelectionModel} this
65345 * @param {Number} rowIndex The selected index
65346 * @param {Roo.data.Record} r The record
65348 "rowselect" : true,
65350 * @event rowdeselect
65351 * Fires when a row is deselected.
65352 * @param {SelectionModel} this
65353 * @param {Number} rowIndex The selected index
65355 "rowdeselect" : true
65357 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
65358 this.locked = false;
65361 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
65363 * @cfg {Boolean} singleSelect
65364 * True to allow selection of only one row at a time (defaults to false)
65366 singleSelect : false,
65369 initEvents : function(){
65371 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
65372 this.grid.on("mousedown", this.handleMouseDown, this);
65373 }else{ // allow click to work like normal
65374 this.grid.on("rowclick", this.handleDragableRowClick, this);
65376 // bootstrap does not have a view..
65377 var view = this.grid.view ? this.grid.view : this.grid;
65378 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
65379 "up" : function(e){
65381 this.selectPrevious(e.shiftKey);
65382 }else if(this.last !== false && this.lastActive !== false){
65383 var last = this.last;
65384 this.selectRange(this.last, this.lastActive-1);
65385 view.focusRow(this.lastActive);
65386 if(last !== false){
65390 this.selectFirstRow();
65392 this.fireEvent("afterselectionchange", this);
65394 "down" : function(e){
65396 this.selectNext(e.shiftKey);
65397 }else if(this.last !== false && this.lastActive !== false){
65398 var last = this.last;
65399 this.selectRange(this.last, this.lastActive+1);
65400 view.focusRow(this.lastActive);
65401 if(last !== false){
65405 this.selectFirstRow();
65407 this.fireEvent("afterselectionchange", this);
65413 view.on("refresh", this.onRefresh, this);
65414 view.on("rowupdated", this.onRowUpdated, this);
65415 view.on("rowremoved", this.onRemove, this);
65419 onRefresh : function(){
65420 var ds = this.grid.ds, i, v = this.grid.view;
65421 var s = this.selections;
65422 s.each(function(r){
65423 if((i = ds.indexOfId(r.id)) != -1){
65425 s.add(ds.getAt(i)); // updating the selection relate data
65433 onRemove : function(v, index, r){
65434 this.selections.remove(r);
65438 onRowUpdated : function(v, index, r){
65439 if(this.isSelected(r)){
65440 v.onRowSelect(index);
65446 * @param {Array} records The records to select
65447 * @param {Boolean} keepExisting (optional) True to keep existing selections
65449 selectRecords : function(records, keepExisting){
65451 this.clearSelections();
65453 var ds = this.grid.ds;
65454 for(var i = 0, len = records.length; i < len; i++){
65455 this.selectRow(ds.indexOf(records[i]), true);
65460 * Gets the number of selected rows.
65463 getCount : function(){
65464 return this.selections.length;
65468 * Selects the first row in the grid.
65470 selectFirstRow : function(){
65475 * Select the last row.
65476 * @param {Boolean} keepExisting (optional) True to keep existing selections
65478 selectLastRow : function(keepExisting){
65479 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
65483 * Selects the row immediately following the last selected row.
65484 * @param {Boolean} keepExisting (optional) True to keep existing selections
65486 selectNext : function(keepExisting){
65487 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
65488 this.selectRow(this.last+1, keepExisting);
65489 var view = this.grid.view ? this.grid.view : this.grid;
65490 view.focusRow(this.last);
65495 * Selects the row that precedes the last selected row.
65496 * @param {Boolean} keepExisting (optional) True to keep existing selections
65498 selectPrevious : function(keepExisting){
65500 this.selectRow(this.last-1, keepExisting);
65501 var view = this.grid.view ? this.grid.view : this.grid;
65502 view.focusRow(this.last);
65507 * Returns the selected records
65508 * @return {Array} Array of selected records
65510 getSelections : function(){
65511 return [].concat(this.selections.items);
65515 * Returns the first selected record.
65518 getSelected : function(){
65519 return this.selections.itemAt(0);
65524 * Clears all selections.
65526 clearSelections : function(fast){
65531 var ds = this.grid.ds;
65532 var s = this.selections;
65533 s.each(function(r){
65534 this.deselectRow(ds.indexOfId(r.id));
65538 this.selections.clear();
65545 * Selects all rows.
65547 selectAll : function(){
65551 this.selections.clear();
65552 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
65553 this.selectRow(i, true);
65558 * Returns True if there is a selection.
65559 * @return {Boolean}
65561 hasSelection : function(){
65562 return this.selections.length > 0;
65566 * Returns True if the specified row is selected.
65567 * @param {Number/Record} record The record or index of the record to check
65568 * @return {Boolean}
65570 isSelected : function(index){
65571 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
65572 return (r && this.selections.key(r.id) ? true : false);
65576 * Returns True if the specified record id is selected.
65577 * @param {String} id The id of record to check
65578 * @return {Boolean}
65580 isIdSelected : function(id){
65581 return (this.selections.key(id) ? true : false);
65585 handleMouseDown : function(e, t)
65587 var view = this.grid.view ? this.grid.view : this.grid;
65589 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
65592 if(e.shiftKey && this.last !== false){
65593 var last = this.last;
65594 this.selectRange(last, rowIndex, e.ctrlKey);
65595 this.last = last; // reset the last
65596 view.focusRow(rowIndex);
65598 var isSelected = this.isSelected(rowIndex);
65599 if(e.button !== 0 && isSelected){
65600 view.focusRow(rowIndex);
65601 }else if(e.ctrlKey && isSelected){
65602 this.deselectRow(rowIndex);
65603 }else if(!isSelected){
65604 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
65605 view.focusRow(rowIndex);
65608 this.fireEvent("afterselectionchange", this);
65611 handleDragableRowClick : function(grid, rowIndex, e)
65613 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
65614 this.selectRow(rowIndex, false);
65615 var view = this.grid.view ? this.grid.view : this.grid;
65616 view.focusRow(rowIndex);
65617 this.fireEvent("afterselectionchange", this);
65622 * Selects multiple rows.
65623 * @param {Array} rows Array of the indexes of the row to select
65624 * @param {Boolean} keepExisting (optional) True to keep existing selections
65626 selectRows : function(rows, keepExisting){
65628 this.clearSelections();
65630 for(var i = 0, len = rows.length; i < len; i++){
65631 this.selectRow(rows[i], true);
65636 * Selects a range of rows. All rows in between startRow and endRow are also selected.
65637 * @param {Number} startRow The index of the first row in the range
65638 * @param {Number} endRow The index of the last row in the range
65639 * @param {Boolean} keepExisting (optional) True to retain existing selections
65641 selectRange : function(startRow, endRow, keepExisting){
65646 this.clearSelections();
65648 if(startRow <= endRow){
65649 for(var i = startRow; i <= endRow; i++){
65650 this.selectRow(i, true);
65653 for(var i = startRow; i >= endRow; i--){
65654 this.selectRow(i, true);
65660 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
65661 * @param {Number} startRow The index of the first row in the range
65662 * @param {Number} endRow The index of the last row in the range
65664 deselectRange : function(startRow, endRow, preventViewNotify){
65668 for(var i = startRow; i <= endRow; i++){
65669 this.deselectRow(i, preventViewNotify);
65675 * @param {Number} row The index of the row to select
65676 * @param {Boolean} keepExisting (optional) True to keep existing selections
65678 selectRow : function(index, keepExisting, preventViewNotify){
65679 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
65682 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
65683 if(!keepExisting || this.singleSelect){
65684 this.clearSelections();
65686 var r = this.grid.ds.getAt(index);
65687 this.selections.add(r);
65688 this.last = this.lastActive = index;
65689 if(!preventViewNotify){
65690 var view = this.grid.view ? this.grid.view : this.grid;
65691 view.onRowSelect(index);
65693 this.fireEvent("rowselect", this, index, r);
65694 this.fireEvent("selectionchange", this);
65700 * @param {Number} row The index of the row to deselect
65702 deselectRow : function(index, preventViewNotify){
65706 if(this.last == index){
65709 if(this.lastActive == index){
65710 this.lastActive = false;
65712 var r = this.grid.ds.getAt(index);
65713 this.selections.remove(r);
65714 if(!preventViewNotify){
65715 var view = this.grid.view ? this.grid.view : this.grid;
65716 view.onRowDeselect(index);
65718 this.fireEvent("rowdeselect", this, index);
65719 this.fireEvent("selectionchange", this);
65723 restoreLast : function(){
65725 this.last = this._last;
65730 acceptsNav : function(row, col, cm){
65731 return !cm.isHidden(col) && cm.isCellEditable(col, row);
65735 onEditorKey : function(field, e){
65736 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
65741 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65743 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65745 }else if(k == e.ENTER && !e.ctrlKey){
65749 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
65751 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
65753 }else if(k == e.ESC){
65757 g.startEditing(newCell[0], newCell[1]);
65762 * Ext JS Library 1.1.1
65763 * Copyright(c) 2006-2007, Ext JS, LLC.
65765 * Originally Released Under LGPL - original licence link has changed is not relivant.
65768 * <script type="text/javascript">
65771 * @class Roo.grid.CellSelectionModel
65772 * @extends Roo.grid.AbstractSelectionModel
65773 * This class provides the basic implementation for cell selection in a grid.
65775 * @param {Object} config The object containing the configuration of this model.
65776 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
65778 Roo.grid.CellSelectionModel = function(config){
65779 Roo.apply(this, config);
65781 this.selection = null;
65785 * @event beforerowselect
65786 * Fires before a cell is selected.
65787 * @param {SelectionModel} this
65788 * @param {Number} rowIndex The selected row index
65789 * @param {Number} colIndex The selected cell index
65791 "beforecellselect" : true,
65793 * @event cellselect
65794 * Fires when a cell is selected.
65795 * @param {SelectionModel} this
65796 * @param {Number} rowIndex The selected row index
65797 * @param {Number} colIndex The selected cell index
65799 "cellselect" : true,
65801 * @event selectionchange
65802 * Fires when the active selection changes.
65803 * @param {SelectionModel} this
65804 * @param {Object} selection null for no selection or an object (o) with two properties
65806 <li>o.record: the record object for the row the selection is in</li>
65807 <li>o.cell: An array of [rowIndex, columnIndex]</li>
65810 "selectionchange" : true,
65813 * Fires when the tab (or enter) was pressed on the last editable cell
65814 * You can use this to trigger add new row.
65815 * @param {SelectionModel} this
65819 * @event beforeeditnext
65820 * Fires before the next editable sell is made active
65821 * You can use this to skip to another cell or fire the tabend
65822 * if you set cell to false
65823 * @param {Object} eventdata object : { cell : [ row, col ] }
65825 "beforeeditnext" : true
65827 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
65830 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
65832 enter_is_tab: false,
65835 initEvents : function(){
65836 this.grid.on("mousedown", this.handleMouseDown, this);
65837 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
65838 var view = this.grid.view;
65839 view.on("refresh", this.onViewChange, this);
65840 view.on("rowupdated", this.onRowUpdated, this);
65841 view.on("beforerowremoved", this.clearSelections, this);
65842 view.on("beforerowsinserted", this.clearSelections, this);
65843 if(this.grid.isEditor){
65844 this.grid.on("beforeedit", this.beforeEdit, this);
65849 beforeEdit : function(e){
65850 this.select(e.row, e.column, false, true, e.record);
65854 onRowUpdated : function(v, index, r){
65855 if(this.selection && this.selection.record == r){
65856 v.onCellSelect(index, this.selection.cell[1]);
65861 onViewChange : function(){
65862 this.clearSelections(true);
65866 * Returns the currently selected cell,.
65867 * @return {Array} The selected cell (row, column) or null if none selected.
65869 getSelectedCell : function(){
65870 return this.selection ? this.selection.cell : null;
65874 * Clears all selections.
65875 * @param {Boolean} true to prevent the gridview from being notified about the change.
65877 clearSelections : function(preventNotify){
65878 var s = this.selection;
65880 if(preventNotify !== true){
65881 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
65883 this.selection = null;
65884 this.fireEvent("selectionchange", this, null);
65889 * Returns true if there is a selection.
65890 * @return {Boolean}
65892 hasSelection : function(){
65893 return this.selection ? true : false;
65897 handleMouseDown : function(e, t){
65898 var v = this.grid.getView();
65899 if(this.isLocked()){
65902 var row = v.findRowIndex(t);
65903 var cell = v.findCellIndex(t);
65904 if(row !== false && cell !== false){
65905 this.select(row, cell);
65911 * @param {Number} rowIndex
65912 * @param {Number} collIndex
65914 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
65915 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
65916 this.clearSelections();
65917 r = r || this.grid.dataSource.getAt(rowIndex);
65920 cell : [rowIndex, colIndex]
65922 if(!preventViewNotify){
65923 var v = this.grid.getView();
65924 v.onCellSelect(rowIndex, colIndex);
65925 if(preventFocus !== true){
65926 v.focusCell(rowIndex, colIndex);
65929 this.fireEvent("cellselect", this, rowIndex, colIndex);
65930 this.fireEvent("selectionchange", this, this.selection);
65935 isSelectable : function(rowIndex, colIndex, cm){
65936 return !cm.isHidden(colIndex);
65940 handleKeyDown : function(e){
65941 //Roo.log('Cell Sel Model handleKeyDown');
65942 if(!e.isNavKeyPress()){
65945 var g = this.grid, s = this.selection;
65948 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
65950 this.select(cell[0], cell[1]);
65955 var walk = function(row, col, step){
65956 return g.walkCells(row, col, step, sm.isSelectable, sm);
65958 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
65965 // handled by onEditorKey
65966 if (g.isEditor && g.editing) {
65970 newCell = walk(r, c-1, -1);
65972 newCell = walk(r, c+1, 1);
65977 newCell = walk(r+1, c, 1);
65981 newCell = walk(r-1, c, -1);
65985 newCell = walk(r, c+1, 1);
65989 newCell = walk(r, c-1, -1);
65994 if(g.isEditor && !g.editing){
65995 g.startEditing(r, c);
66004 this.select(newCell[0], newCell[1]);
66010 acceptsNav : function(row, col, cm){
66011 return !cm.isHidden(col) && cm.isCellEditable(col, row);
66015 * @param {Number} field (not used) - as it's normally used as a listener
66016 * @param {Number} e - event - fake it by using
66018 * var e = Roo.EventObjectImpl.prototype;
66019 * e.keyCode = e.TAB
66023 onEditorKey : function(field, e){
66025 var k = e.getKey(),
66028 ed = g.activeEditor,
66030 ///Roo.log('onEditorKey' + k);
66033 if (this.enter_is_tab && k == e.ENTER) {
66039 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
66041 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
66047 } else if(k == e.ENTER && !e.ctrlKey){
66050 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
66052 } else if(k == e.ESC){
66057 var ecall = { cell : newCell, forward : forward };
66058 this.fireEvent('beforeeditnext', ecall );
66059 newCell = ecall.cell;
66060 forward = ecall.forward;
66064 //Roo.log('next cell after edit');
66065 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
66066 } else if (forward) {
66067 // tabbed past last
66068 this.fireEvent.defer(100, this, ['tabend',this]);
66073 * Ext JS Library 1.1.1
66074 * Copyright(c) 2006-2007, Ext JS, LLC.
66076 * Originally Released Under LGPL - original licence link has changed is not relivant.
66079 * <script type="text/javascript">
66083 * @class Roo.grid.EditorGrid
66084 * @extends Roo.grid.Grid
66085 * Class for creating and editable grid.
66086 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66087 * The container MUST have some type of size defined for the grid to fill. The container will be
66088 * automatically set to position relative if it isn't already.
66089 * @param {Object} dataSource The data model to bind to
66090 * @param {Object} colModel The column model with info about this grid's columns
66092 Roo.grid.EditorGrid = function(container, config){
66093 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
66094 this.getGridEl().addClass("xedit-grid");
66096 if(!this.selModel){
66097 this.selModel = new Roo.grid.CellSelectionModel();
66100 this.activeEditor = null;
66104 * @event beforeedit
66105 * Fires before cell editing is triggered. The edit event object has the following properties <br />
66106 * <ul style="padding:5px;padding-left:16px;">
66107 * <li>grid - This grid</li>
66108 * <li>record - The record being edited</li>
66109 * <li>field - The field name being edited</li>
66110 * <li>value - The value for the field being edited.</li>
66111 * <li>row - The grid row index</li>
66112 * <li>column - The grid column index</li>
66113 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
66115 * @param {Object} e An edit event (see above for description)
66117 "beforeedit" : true,
66120 * Fires after a cell is edited. <br />
66121 * <ul style="padding:5px;padding-left:16px;">
66122 * <li>grid - This grid</li>
66123 * <li>record - The record being edited</li>
66124 * <li>field - The field name being edited</li>
66125 * <li>value - The value being set</li>
66126 * <li>originalValue - The original value for the field, before the edit.</li>
66127 * <li>row - The grid row index</li>
66128 * <li>column - The grid column index</li>
66130 * @param {Object} e An edit event (see above for description)
66132 "afteredit" : true,
66134 * @event validateedit
66135 * Fires after a cell is edited, but before the value is set in the record.
66136 * You can use this to modify the value being set in the field, Return false
66137 * to cancel the change. The edit event object has the following properties <br />
66138 * <ul style="padding:5px;padding-left:16px;">
66139 * <li>editor - This editor</li>
66140 * <li>grid - This grid</li>
66141 * <li>record - The record being edited</li>
66142 * <li>field - The field name being edited</li>
66143 * <li>value - The value being set</li>
66144 * <li>originalValue - The original value for the field, before the edit.</li>
66145 * <li>row - The grid row index</li>
66146 * <li>column - The grid column index</li>
66147 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
66149 * @param {Object} e An edit event (see above for description)
66151 "validateedit" : true
66153 this.on("bodyscroll", this.stopEditing, this);
66154 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
66157 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
66159 * @cfg {Number} clicksToEdit
66160 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
66167 trackMouseOver: false, // causes very odd FF errors
66169 onCellDblClick : function(g, row, col){
66170 this.startEditing(row, col);
66173 onEditComplete : function(ed, value, startValue){
66174 this.editing = false;
66175 this.activeEditor = null;
66176 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
66178 var field = this.colModel.getDataIndex(ed.col);
66183 originalValue: startValue,
66190 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
66193 if(String(value) !== String(startValue)){
66195 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
66196 r.set(field, e.value);
66197 // if we are dealing with a combo box..
66198 // then we also set the 'name' colum to be the displayField
66199 if (ed.field.displayField && ed.field.name) {
66200 r.set(ed.field.name, ed.field.el.dom.value);
66203 delete e.cancel; //?? why!!!
66204 this.fireEvent("afteredit", e);
66207 this.fireEvent("afteredit", e); // always fire it!
66209 this.view.focusCell(ed.row, ed.col);
66213 * Starts editing the specified for the specified row/column
66214 * @param {Number} rowIndex
66215 * @param {Number} colIndex
66217 startEditing : function(row, col){
66218 this.stopEditing();
66219 if(this.colModel.isCellEditable(col, row)){
66220 this.view.ensureVisible(row, col, true);
66222 var r = this.dataSource.getAt(row);
66223 var field = this.colModel.getDataIndex(col);
66224 var cell = Roo.get(this.view.getCell(row,col));
66229 value: r.data[field],
66234 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
66235 this.editing = true;
66236 var ed = this.colModel.getCellEditor(col, row);
66242 ed.render(ed.parentEl || document.body);
66248 (function(){ // complex but required for focus issues in safari, ie and opera
66252 ed.on("complete", this.onEditComplete, this, {single: true});
66253 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
66254 this.activeEditor = ed;
66255 var v = r.data[field];
66256 ed.startEdit(this.view.getCell(row, col), v);
66257 // combo's with 'displayField and name set
66258 if (ed.field.displayField && ed.field.name) {
66259 ed.field.el.dom.value = r.data[ed.field.name];
66263 }).defer(50, this);
66269 * Stops any active editing
66271 stopEditing : function(){
66272 if(this.activeEditor){
66273 this.activeEditor.completeEdit();
66275 this.activeEditor = null;
66279 * Called to get grid's drag proxy text, by default returns this.ddText.
66282 getDragDropText : function(){
66283 var count = this.selModel.getSelectedCell() ? 1 : 0;
66284 return String.format(this.ddText, count, count == 1 ? '' : 's');
66289 * Ext JS Library 1.1.1
66290 * Copyright(c) 2006-2007, Ext JS, LLC.
66292 * Originally Released Under LGPL - original licence link has changed is not relivant.
66295 * <script type="text/javascript">
66298 // private - not really -- you end up using it !
66299 // This is a support class used internally by the Grid components
66302 * @class Roo.grid.GridEditor
66303 * @extends Roo.Editor
66304 * Class for creating and editable grid elements.
66305 * @param {Object} config any settings (must include field)
66307 Roo.grid.GridEditor = function(field, config){
66308 if (!config && field.field) {
66310 field = Roo.factory(config.field, Roo.form);
66312 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
66313 field.monitorTab = false;
66316 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
66319 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
66322 alignment: "tl-tl",
66325 cls: "x-small-editor x-grid-editor",
66330 * Ext JS Library 1.1.1
66331 * Copyright(c) 2006-2007, Ext JS, LLC.
66333 * Originally Released Under LGPL - original licence link has changed is not relivant.
66336 * <script type="text/javascript">
66341 Roo.grid.PropertyRecord = Roo.data.Record.create([
66342 {name:'name',type:'string'}, 'value'
66346 Roo.grid.PropertyStore = function(grid, source){
66348 this.store = new Roo.data.Store({
66349 recordType : Roo.grid.PropertyRecord
66351 this.store.on('update', this.onUpdate, this);
66353 this.setSource(source);
66355 Roo.grid.PropertyStore.superclass.constructor.call(this);
66360 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
66361 setSource : function(o){
66363 this.store.removeAll();
66366 if(this.isEditableValue(o[k])){
66367 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
66370 this.store.loadRecords({records: data}, {}, true);
66373 onUpdate : function(ds, record, type){
66374 if(type == Roo.data.Record.EDIT){
66375 var v = record.data['value'];
66376 var oldValue = record.modified['value'];
66377 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
66378 this.source[record.id] = v;
66380 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
66387 getProperty : function(row){
66388 return this.store.getAt(row);
66391 isEditableValue: function(val){
66392 if(val && val instanceof Date){
66394 }else if(typeof val == 'object' || typeof val == 'function'){
66400 setValue : function(prop, value){
66401 this.source[prop] = value;
66402 this.store.getById(prop).set('value', value);
66405 getSource : function(){
66406 return this.source;
66410 Roo.grid.PropertyColumnModel = function(grid, store){
66413 g.PropertyColumnModel.superclass.constructor.call(this, [
66414 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
66415 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
66417 this.store = store;
66418 this.bselect = Roo.DomHelper.append(document.body, {
66419 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
66420 {tag: 'option', value: 'true', html: 'true'},
66421 {tag: 'option', value: 'false', html: 'false'}
66424 Roo.id(this.bselect);
66427 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
66428 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
66429 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
66430 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
66431 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
66433 this.renderCellDelegate = this.renderCell.createDelegate(this);
66434 this.renderPropDelegate = this.renderProp.createDelegate(this);
66437 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
66441 valueText : 'Value',
66443 dateFormat : 'm/j/Y',
66446 renderDate : function(dateVal){
66447 return dateVal.dateFormat(this.dateFormat);
66450 renderBool : function(bVal){
66451 return bVal ? 'true' : 'false';
66454 isCellEditable : function(colIndex, rowIndex){
66455 return colIndex == 1;
66458 getRenderer : function(col){
66460 this.renderCellDelegate : this.renderPropDelegate;
66463 renderProp : function(v){
66464 return this.getPropertyName(v);
66467 renderCell : function(val){
66469 if(val instanceof Date){
66470 rv = this.renderDate(val);
66471 }else if(typeof val == 'boolean'){
66472 rv = this.renderBool(val);
66474 return Roo.util.Format.htmlEncode(rv);
66477 getPropertyName : function(name){
66478 var pn = this.grid.propertyNames;
66479 return pn && pn[name] ? pn[name] : name;
66482 getCellEditor : function(colIndex, rowIndex){
66483 var p = this.store.getProperty(rowIndex);
66484 var n = p.data['name'], val = p.data['value'];
66486 if(typeof(this.grid.customEditors[n]) == 'string'){
66487 return this.editors[this.grid.customEditors[n]];
66489 if(typeof(this.grid.customEditors[n]) != 'undefined'){
66490 return this.grid.customEditors[n];
66492 if(val instanceof Date){
66493 return this.editors['date'];
66494 }else if(typeof val == 'number'){
66495 return this.editors['number'];
66496 }else if(typeof val == 'boolean'){
66497 return this.editors['boolean'];
66499 return this.editors['string'];
66505 * @class Roo.grid.PropertyGrid
66506 * @extends Roo.grid.EditorGrid
66507 * This class represents the interface of a component based property grid control.
66508 * <br><br>Usage:<pre><code>
66509 var grid = new Roo.grid.PropertyGrid("my-container-id", {
66517 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66518 * The container MUST have some type of size defined for the grid to fill. The container will be
66519 * automatically set to position relative if it isn't already.
66520 * @param {Object} config A config object that sets properties on this grid.
66522 Roo.grid.PropertyGrid = function(container, config){
66523 config = config || {};
66524 var store = new Roo.grid.PropertyStore(this);
66525 this.store = store;
66526 var cm = new Roo.grid.PropertyColumnModel(this, store);
66527 store.store.sort('name', 'ASC');
66528 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
66531 enableColLock:false,
66532 enableColumnMove:false,
66534 trackMouseOver: false,
66537 this.getGridEl().addClass('x-props-grid');
66538 this.lastEditRow = null;
66539 this.on('columnresize', this.onColumnResize, this);
66542 * @event beforepropertychange
66543 * Fires before a property changes (return false to stop?)
66544 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66545 * @param {String} id Record Id
66546 * @param {String} newval New Value
66547 * @param {String} oldval Old Value
66549 "beforepropertychange": true,
66551 * @event propertychange
66552 * Fires after a property changes
66553 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66554 * @param {String} id Record Id
66555 * @param {String} newval New Value
66556 * @param {String} oldval Old Value
66558 "propertychange": true
66560 this.customEditors = this.customEditors || {};
66562 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
66565 * @cfg {Object} customEditors map of colnames=> custom editors.
66566 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
66567 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
66568 * false disables editing of the field.
66572 * @cfg {Object} propertyNames map of property Names to their displayed value
66575 render : function(){
66576 Roo.grid.PropertyGrid.superclass.render.call(this);
66577 this.autoSize.defer(100, this);
66580 autoSize : function(){
66581 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
66583 this.view.fitColumns();
66587 onColumnResize : function(){
66588 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
66592 * Sets the data for the Grid
66593 * accepts a Key => Value object of all the elements avaiable.
66594 * @param {Object} data to appear in grid.
66596 setSource : function(source){
66597 this.store.setSource(source);
66601 * Gets all the data from the grid.
66602 * @return {Object} data data stored in grid
66604 getSource : function(){
66605 return this.store.getSource();
66614 * @class Roo.grid.Calendar
66615 * @extends Roo.grid.Grid
66616 * This class extends the Grid to provide a calendar widget
66617 * <br><br>Usage:<pre><code>
66618 var grid = new Roo.grid.Calendar("my-container-id", {
66621 selModel: mySelectionModel,
66622 autoSizeColumns: true,
66623 monitorWindowResize: false,
66624 trackMouseOver: true
66625 eventstore : real data store..
66631 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66632 * The container MUST have some type of size defined for the grid to fill. The container will be
66633 * automatically set to position relative if it isn't already.
66634 * @param {Object} config A config object that sets properties on this grid.
66636 Roo.grid.Calendar = function(container, config){
66637 // initialize the container
66638 this.container = Roo.get(container);
66639 this.container.update("");
66640 this.container.setStyle("overflow", "hidden");
66641 this.container.addClass('x-grid-container');
66643 this.id = this.container.id;
66645 Roo.apply(this, config);
66646 // check and correct shorthanded configs
66650 for (var r = 0;r < 6;r++) {
66653 for (var c =0;c < 7;c++) {
66657 if (this.eventStore) {
66658 this.eventStore= Roo.factory(this.eventStore, Roo.data);
66659 this.eventStore.on('load',this.onLoad, this);
66660 this.eventStore.on('beforeload',this.clearEvents, this);
66664 this.dataSource = new Roo.data.Store({
66665 proxy: new Roo.data.MemoryProxy(rows),
66666 reader: new Roo.data.ArrayReader({}, [
66667 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
66670 this.dataSource.load();
66671 this.ds = this.dataSource;
66672 this.ds.xmodule = this.xmodule || false;
66675 var cellRender = function(v,x,r)
66677 return String.format(
66678 '<div class="fc-day fc-widget-content"><div>' +
66679 '<div class="fc-event-container"></div>' +
66680 '<div class="fc-day-number">{0}</div>'+
66682 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
66683 '</div></div>', v);
66688 this.colModel = new Roo.grid.ColumnModel( [
66690 xtype: 'ColumnModel',
66692 dataIndex : 'weekday0',
66694 renderer : cellRender
66697 xtype: 'ColumnModel',
66699 dataIndex : 'weekday1',
66701 renderer : cellRender
66704 xtype: 'ColumnModel',
66706 dataIndex : 'weekday2',
66707 header : 'Tuesday',
66708 renderer : cellRender
66711 xtype: 'ColumnModel',
66713 dataIndex : 'weekday3',
66714 header : 'Wednesday',
66715 renderer : cellRender
66718 xtype: 'ColumnModel',
66720 dataIndex : 'weekday4',
66721 header : 'Thursday',
66722 renderer : cellRender
66725 xtype: 'ColumnModel',
66727 dataIndex : 'weekday5',
66729 renderer : cellRender
66732 xtype: 'ColumnModel',
66734 dataIndex : 'weekday6',
66735 header : 'Saturday',
66736 renderer : cellRender
66739 this.cm = this.colModel;
66740 this.cm.xmodule = this.xmodule || false;
66744 //this.selModel = new Roo.grid.CellSelectionModel();
66745 //this.sm = this.selModel;
66746 //this.selModel.init(this);
66750 this.container.setWidth(this.width);
66754 this.container.setHeight(this.height);
66761 * The raw click event for the entire grid.
66762 * @param {Roo.EventObject} e
66767 * The raw dblclick event for the entire grid.
66768 * @param {Roo.EventObject} e
66772 * @event contextmenu
66773 * The raw contextmenu event for the entire grid.
66774 * @param {Roo.EventObject} e
66776 "contextmenu" : true,
66779 * The raw mousedown event for the entire grid.
66780 * @param {Roo.EventObject} e
66782 "mousedown" : true,
66785 * The raw mouseup event for the entire grid.
66786 * @param {Roo.EventObject} e
66791 * The raw mouseover event for the entire grid.
66792 * @param {Roo.EventObject} e
66794 "mouseover" : true,
66797 * The raw mouseout event for the entire grid.
66798 * @param {Roo.EventObject} e
66803 * The raw keypress event for the entire grid.
66804 * @param {Roo.EventObject} e
66809 * The raw keydown event for the entire grid.
66810 * @param {Roo.EventObject} e
66818 * Fires when a cell is clicked
66819 * @param {Grid} this
66820 * @param {Number} rowIndex
66821 * @param {Number} columnIndex
66822 * @param {Roo.EventObject} e
66824 "cellclick" : true,
66826 * @event celldblclick
66827 * Fires when a cell is double clicked
66828 * @param {Grid} this
66829 * @param {Number} rowIndex
66830 * @param {Number} columnIndex
66831 * @param {Roo.EventObject} e
66833 "celldblclick" : true,
66836 * Fires when a row is clicked
66837 * @param {Grid} this
66838 * @param {Number} rowIndex
66839 * @param {Roo.EventObject} e
66843 * @event rowdblclick
66844 * Fires when a row is double clicked
66845 * @param {Grid} this
66846 * @param {Number} rowIndex
66847 * @param {Roo.EventObject} e
66849 "rowdblclick" : true,
66851 * @event headerclick
66852 * Fires when a header is clicked
66853 * @param {Grid} this
66854 * @param {Number} columnIndex
66855 * @param {Roo.EventObject} e
66857 "headerclick" : true,
66859 * @event headerdblclick
66860 * Fires when a header cell is double clicked
66861 * @param {Grid} this
66862 * @param {Number} columnIndex
66863 * @param {Roo.EventObject} e
66865 "headerdblclick" : true,
66867 * @event rowcontextmenu
66868 * Fires when a row is right clicked
66869 * @param {Grid} this
66870 * @param {Number} rowIndex
66871 * @param {Roo.EventObject} e
66873 "rowcontextmenu" : true,
66875 * @event cellcontextmenu
66876 * Fires when a cell is right clicked
66877 * @param {Grid} this
66878 * @param {Number} rowIndex
66879 * @param {Number} cellIndex
66880 * @param {Roo.EventObject} e
66882 "cellcontextmenu" : true,
66884 * @event headercontextmenu
66885 * Fires when a header is right clicked
66886 * @param {Grid} this
66887 * @param {Number} columnIndex
66888 * @param {Roo.EventObject} e
66890 "headercontextmenu" : true,
66892 * @event bodyscroll
66893 * Fires when the body element is scrolled
66894 * @param {Number} scrollLeft
66895 * @param {Number} scrollTop
66897 "bodyscroll" : true,
66899 * @event columnresize
66900 * Fires when the user resizes a column
66901 * @param {Number} columnIndex
66902 * @param {Number} newSize
66904 "columnresize" : true,
66906 * @event columnmove
66907 * Fires when the user moves a column
66908 * @param {Number} oldIndex
66909 * @param {Number} newIndex
66911 "columnmove" : true,
66914 * Fires when row(s) start being dragged
66915 * @param {Grid} this
66916 * @param {Roo.GridDD} dd The drag drop object
66917 * @param {event} e The raw browser event
66919 "startdrag" : true,
66922 * Fires when a drag operation is complete
66923 * @param {Grid} this
66924 * @param {Roo.GridDD} dd The drag drop object
66925 * @param {event} e The raw browser event
66930 * Fires when dragged row(s) are dropped on a valid DD target
66931 * @param {Grid} this
66932 * @param {Roo.GridDD} dd The drag drop object
66933 * @param {String} targetId The target drag drop object
66934 * @param {event} e The raw browser event
66939 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
66940 * @param {Grid} this
66941 * @param {Roo.GridDD} dd The drag drop object
66942 * @param {String} targetId The target drag drop object
66943 * @param {event} e The raw browser event
66948 * Fires when the dragged row(s) first cross another DD target while being dragged
66949 * @param {Grid} this
66950 * @param {Roo.GridDD} dd The drag drop object
66951 * @param {String} targetId The target drag drop object
66952 * @param {event} e The raw browser event
66954 "dragenter" : true,
66957 * Fires when the dragged row(s) leave another DD target while being dragged
66958 * @param {Grid} this
66959 * @param {Roo.GridDD} dd The drag drop object
66960 * @param {String} targetId The target drag drop object
66961 * @param {event} e The raw browser event
66966 * Fires when a row is rendered, so you can change add a style to it.
66967 * @param {GridView} gridview The grid view
66968 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
66974 * Fires when the grid is rendered
66975 * @param {Grid} grid
66980 * Fires when a date is selected
66981 * @param {DatePicker} this
66982 * @param {Date} date The selected date
66986 * @event monthchange
66987 * Fires when the displayed month changes
66988 * @param {DatePicker} this
66989 * @param {Date} date The selected month
66991 'monthchange': true,
66993 * @event evententer
66994 * Fires when mouse over an event
66995 * @param {Calendar} this
66996 * @param {event} Event
66998 'evententer': true,
67000 * @event eventleave
67001 * Fires when the mouse leaves an
67002 * @param {Calendar} this
67005 'eventleave': true,
67007 * @event eventclick
67008 * Fires when the mouse click an
67009 * @param {Calendar} this
67012 'eventclick': true,
67014 * @event eventrender
67015 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
67016 * @param {Calendar} this
67017 * @param {data} data to be modified
67019 'eventrender': true
67023 Roo.grid.Grid.superclass.constructor.call(this);
67024 this.on('render', function() {
67025 this.view.el.addClass('x-grid-cal');
67027 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
67031 if (!Roo.grid.Calendar.style) {
67032 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
67035 '.x-grid-cal .x-grid-col' : {
67036 height: 'auto !important',
67037 'vertical-align': 'top'
67039 '.x-grid-cal .fc-event-hori' : {
67050 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
67052 * @cfg {Store} eventStore The store that loads events.
67057 activeDate : false,
67060 monitorWindowResize : false,
67063 resizeColumns : function() {
67064 var col = (this.view.el.getWidth() / 7) - 3;
67065 // loop through cols, and setWidth
67066 for(var i =0 ; i < 7 ; i++){
67067 this.cm.setColumnWidth(i, col);
67070 setDate :function(date) {
67072 Roo.log('setDate?');
67074 this.resizeColumns();
67075 var vd = this.activeDate;
67076 this.activeDate = date;
67077 // if(vd && this.el){
67078 // var t = date.getTime();
67079 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
67080 // Roo.log('using add remove');
67082 // this.fireEvent('monthchange', this, date);
67084 // this.cells.removeClass("fc-state-highlight");
67085 // this.cells.each(function(c){
67086 // if(c.dateValue == t){
67087 // c.addClass("fc-state-highlight");
67088 // setTimeout(function(){
67089 // try{c.dom.firstChild.focus();}catch(e){}
67099 var days = date.getDaysInMonth();
67101 var firstOfMonth = date.getFirstDateOfMonth();
67102 var startingPos = firstOfMonth.getDay()-this.startDay;
67104 if(startingPos < this.startDay){
67108 var pm = date.add(Date.MONTH, -1);
67109 var prevStart = pm.getDaysInMonth()-startingPos;
67113 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
67115 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
67116 //this.cells.addClassOnOver('fc-state-hover');
67118 var cells = this.cells.elements;
67119 var textEls = this.textNodes;
67121 //Roo.each(cells, function(cell){
67122 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
67125 days += startingPos;
67127 // convert everything to numbers so it's fast
67128 var day = 86400000;
67129 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
67132 //Roo.log(prevStart);
67134 var today = new Date().clearTime().getTime();
67135 var sel = date.clearTime().getTime();
67136 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
67137 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
67138 var ddMatch = this.disabledDatesRE;
67139 var ddText = this.disabledDatesText;
67140 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
67141 var ddaysText = this.disabledDaysText;
67142 var format = this.format;
67144 var setCellClass = function(cal, cell){
67146 //Roo.log('set Cell Class');
67148 var t = d.getTime();
67153 cell.dateValue = t;
67155 cell.className += " fc-today";
67156 cell.className += " fc-state-highlight";
67157 cell.title = cal.todayText;
67160 // disable highlight in other month..
67161 cell.className += " fc-state-highlight";
67166 //cell.className = " fc-state-disabled";
67167 cell.title = cal.minText;
67171 //cell.className = " fc-state-disabled";
67172 cell.title = cal.maxText;
67176 if(ddays.indexOf(d.getDay()) != -1){
67177 // cell.title = ddaysText;
67178 // cell.className = " fc-state-disabled";
67181 if(ddMatch && format){
67182 var fvalue = d.dateFormat(format);
67183 if(ddMatch.test(fvalue)){
67184 cell.title = ddText.replace("%0", fvalue);
67185 cell.className = " fc-state-disabled";
67189 if (!cell.initialClassName) {
67190 cell.initialClassName = cell.dom.className;
67193 cell.dom.className = cell.initialClassName + ' ' + cell.className;
67198 for(; i < startingPos; i++) {
67199 cells[i].dayName = (++prevStart);
67200 Roo.log(textEls[i]);
67201 d.setDate(d.getDate()+1);
67203 //cells[i].className = "fc-past fc-other-month";
67204 setCellClass(this, cells[i]);
67209 for(; i < days; i++){
67210 intDay = i - startingPos + 1;
67211 cells[i].dayName = (intDay);
67212 d.setDate(d.getDate()+1);
67214 cells[i].className = ''; // "x-date-active";
67215 setCellClass(this, cells[i]);
67219 for(; i < 42; i++) {
67220 //textEls[i].innerHTML = (++extraDays);
67222 d.setDate(d.getDate()+1);
67223 cells[i].dayName = (++extraDays);
67224 cells[i].className = "fc-future fc-other-month";
67225 setCellClass(this, cells[i]);
67228 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
67230 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
67232 // this will cause all the cells to mis
67235 for (var r = 0;r < 6;r++) {
67236 for (var c =0;c < 7;c++) {
67237 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
67241 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
67242 for(i=0;i<cells.length;i++) {
67244 this.cells.elements[i].dayName = cells[i].dayName ;
67245 this.cells.elements[i].className = cells[i].className;
67246 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
67247 this.cells.elements[i].title = cells[i].title ;
67248 this.cells.elements[i].dateValue = cells[i].dateValue ;
67254 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
67255 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
67257 ////if(totalRows != 6){
67258 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
67259 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
67262 this.fireEvent('monthchange', this, date);
67267 * Returns the grid's SelectionModel.
67268 * @return {SelectionModel}
67270 getSelectionModel : function(){
67271 if(!this.selModel){
67272 this.selModel = new Roo.grid.CellSelectionModel();
67274 return this.selModel;
67278 this.eventStore.load()
67284 findCell : function(dt) {
67285 dt = dt.clearTime().getTime();
67287 this.cells.each(function(c){
67288 //Roo.log("check " +c.dateValue + '?=' + dt);
67289 if(c.dateValue == dt){
67299 findCells : function(rec) {
67300 var s = rec.data.start_dt.clone().clearTime().getTime();
67302 var e= rec.data.end_dt.clone().clearTime().getTime();
67305 this.cells.each(function(c){
67306 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
67308 if(c.dateValue > e){
67311 if(c.dateValue < s){
67320 findBestRow: function(cells)
67324 for (var i =0 ; i < cells.length;i++) {
67325 ret = Math.max(cells[i].rows || 0,ret);
67332 addItem : function(rec)
67334 // look for vertical location slot in
67335 var cells = this.findCells(rec);
67337 rec.row = this.findBestRow(cells);
67339 // work out the location.
67343 for(var i =0; i < cells.length; i++) {
67351 if (crow.start.getY() == cells[i].getY()) {
67353 crow.end = cells[i];
67369 for (var i = 0; i < cells.length;i++) {
67370 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
67377 clearEvents: function() {
67379 if (!this.eventStore.getCount()) {
67382 // reset number of rows in cells.
67383 Roo.each(this.cells.elements, function(c){
67387 this.eventStore.each(function(e) {
67388 this.clearEvent(e);
67393 clearEvent : function(ev)
67396 Roo.each(ev.els, function(el) {
67397 el.un('mouseenter' ,this.onEventEnter, this);
67398 el.un('mouseleave' ,this.onEventLeave, this);
67406 renderEvent : function(ev,ctr) {
67408 ctr = this.view.el.select('.fc-event-container',true).first();
67412 this.clearEvent(ev);
67418 var cells = ev.cells;
67419 var rows = ev.rows;
67420 this.fireEvent('eventrender', this, ev);
67422 for(var i =0; i < rows.length; i++) {
67426 cls += ' fc-event-start';
67428 if ((i+1) == rows.length) {
67429 cls += ' fc-event-end';
67432 //Roo.log(ev.data);
67433 // how many rows should it span..
67434 var cg = this.eventTmpl.append(ctr,Roo.apply({
67437 }, ev.data) , true);
67440 cg.on('mouseenter' ,this.onEventEnter, this, ev);
67441 cg.on('mouseleave' ,this.onEventLeave, this, ev);
67442 cg.on('click', this.onEventClick, this, ev);
67446 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
67447 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
67450 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
67451 cg.setWidth(ebox.right - sbox.x -2);
67455 renderEvents: function()
67457 // first make sure there is enough space..
67459 if (!this.eventTmpl) {
67460 this.eventTmpl = new Roo.Template(
67461 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
67462 '<div class="fc-event-inner">' +
67463 '<span class="fc-event-time">{time}</span>' +
67464 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
67466 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
67474 this.cells.each(function(c) {
67475 //Roo.log(c.select('.fc-day-content div',true).first());
67476 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
67479 var ctr = this.view.el.select('.fc-event-container',true).first();
67482 this.eventStore.each(function(ev){
67484 this.renderEvent(ev);
67488 this.view.layout();
67492 onEventEnter: function (e, el,event,d) {
67493 this.fireEvent('evententer', this, el, event);
67496 onEventLeave: function (e, el,event,d) {
67497 this.fireEvent('eventleave', this, el, event);
67500 onEventClick: function (e, el,event,d) {
67501 this.fireEvent('eventclick', this, el, event);
67504 onMonthChange: function () {
67508 onLoad: function () {
67510 //Roo.log('calendar onload');
67512 if(this.eventStore.getCount() > 0){
67516 this.eventStore.each(function(d){
67521 if (typeof(add.end_dt) == 'undefined') {
67522 Roo.log("Missing End time in calendar data: ");
67526 if (typeof(add.start_dt) == 'undefined') {
67527 Roo.log("Missing Start time in calendar data: ");
67531 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
67532 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
67533 add.id = add.id || d.id;
67534 add.title = add.title || '??';
67542 this.renderEvents();
67552 render : function ()
67556 if (!this.view.el.hasClass('course-timesheet')) {
67557 this.view.el.addClass('course-timesheet');
67559 if (this.tsStyle) {
67564 Roo.log(_this.grid.view.el.getWidth());
67567 this.tsStyle = Roo.util.CSS.createStyleSheet({
67568 '.course-timesheet .x-grid-row' : {
67571 '.x-grid-row td' : {
67572 'vertical-align' : 0
67574 '.course-edit-link' : {
67576 'text-overflow' : 'ellipsis',
67577 'overflow' : 'hidden',
67578 'white-space' : 'nowrap',
67579 'cursor' : 'pointer'
67584 '.de-act-sup-link' : {
67585 'color' : 'purple',
67586 'text-decoration' : 'line-through'
67590 'text-decoration' : 'line-through'
67592 '.course-timesheet .course-highlight' : {
67593 'border-top-style': 'dashed !important',
67594 'border-bottom-bottom': 'dashed !important'
67596 '.course-timesheet .course-item' : {
67597 'font-family' : 'tahoma, arial, helvetica',
67598 'font-size' : '11px',
67599 'overflow' : 'hidden',
67600 'padding-left' : '10px',
67601 'padding-right' : '10px',
67602 'padding-top' : '10px'
67610 monitorWindowResize : false,
67611 cellrenderer : function(v,x,r)
67616 xtype: 'CellSelectionModel',
67623 beforeload : function (_self, options)
67625 options.params = options.params || {};
67626 options.params._month = _this.monthField.getValue();
67627 options.params.limit = 9999;
67628 options.params['sort'] = 'when_dt';
67629 options.params['dir'] = 'ASC';
67630 this.proxy.loadResponse = this.loadResponse;
67632 //this.addColumns();
67634 load : function (_self, records, options)
67636 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
67637 // if you click on the translation.. you can edit it...
67638 var el = Roo.get(this);
67639 var id = el.dom.getAttribute('data-id');
67640 var d = el.dom.getAttribute('data-date');
67641 var t = el.dom.getAttribute('data-time');
67642 //var id = this.child('span').dom.textContent;
67645 Pman.Dialog.CourseCalendar.show({
67649 productitem_active : id ? 1 : 0
67651 _this.grid.ds.load({});
67656 _this.panel.fireEvent('resize', [ '', '' ]);
67659 loadResponse : function(o, success, response){
67660 // this is overridden on before load..
67662 Roo.log("our code?");
67663 //Roo.log(success);
67664 //Roo.log(response)
67665 delete this.activeRequest;
67667 this.fireEvent("loadexception", this, o, response);
67668 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67673 result = o.reader.read(response);
67675 Roo.log("load exception?");
67676 this.fireEvent("loadexception", this, o, response, e);
67677 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67680 Roo.log("ready...");
67681 // loop through result.records;
67682 // and set this.tdate[date] = [] << array of records..
67684 Roo.each(result.records, function(r){
67686 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
67687 _this.tdata[r.data.when_dt.format('j')] = [];
67689 _this.tdata[r.data.when_dt.format('j')].push(r.data);
67692 //Roo.log(_this.tdata);
67694 result.records = [];
67695 result.totalRecords = 6;
67697 // let's generate some duumy records for the rows.
67698 //var st = _this.dateField.getValue();
67700 // work out monday..
67701 //st = st.add(Date.DAY, -1 * st.format('w'));
67703 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67705 var firstOfMonth = date.getFirstDayOfMonth();
67706 var days = date.getDaysInMonth();
67708 var firstAdded = false;
67709 for (var i = 0; i < result.totalRecords ; i++) {
67710 //var d= st.add(Date.DAY, i);
67713 for(var w = 0 ; w < 7 ; w++){
67714 if(!firstAdded && firstOfMonth != w){
67721 var dd = (d > 0 && d < 10) ? "0"+d : d;
67722 row['weekday'+w] = String.format(
67723 '<span style="font-size: 16px;"><b>{0}</b></span>'+
67724 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
67726 date.format('Y-m-')+dd
67729 if(typeof(_this.tdata[d]) != 'undefined'){
67730 Roo.each(_this.tdata[d], function(r){
67734 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
67735 if(r.parent_id*1>0){
67736 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
67739 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
67740 deactive = 'de-act-link';
67743 row['weekday'+w] += String.format(
67744 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
67746 r.product_id_name, //1
67747 r.when_dt.format('h:ia'), //2
67757 // only do this if something added..
67759 result.records.push(_this.grid.dataSource.reader.newRow(row));
67763 // push it twice. (second one with an hour..
67767 this.fireEvent("load", this, o, o.request.arg);
67768 o.request.callback.call(o.request.scope, result, o.request.arg, true);
67770 sortInfo : {field: 'when_dt', direction : 'ASC' },
67772 xtype: 'HttpProxy',
67775 url : baseURL + '/Roo/Shop_course.php'
67778 xtype: 'JsonReader',
67795 'name': 'parent_id',
67799 'name': 'product_id',
67803 'name': 'productitem_id',
67821 click : function (_self, e)
67823 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67824 sd.setMonth(sd.getMonth()-1);
67825 _this.monthField.setValue(sd.format('Y-m-d'));
67826 _this.grid.ds.load({});
67832 xtype: 'Separator',
67836 xtype: 'MonthField',
67839 render : function (_self)
67841 _this.monthField = _self;
67842 // _this.monthField.set today
67844 select : function (combo, date)
67846 _this.grid.ds.load({});
67849 value : (function() { return new Date(); })()
67852 xtype: 'Separator',
67858 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
67868 click : function (_self, e)
67870 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67871 sd.setMonth(sd.getMonth()+1);
67872 _this.monthField.setValue(sd.format('Y-m-d'));
67873 _this.grid.ds.load({});
67886 * Ext JS Library 1.1.1
67887 * Copyright(c) 2006-2007, Ext JS, LLC.
67889 * Originally Released Under LGPL - original licence link has changed is not relivant.
67892 * <script type="text/javascript">
67896 * @class Roo.LoadMask
67897 * A simple utility class for generically masking elements while loading data. If the element being masked has
67898 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
67899 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
67900 * element's UpdateManager load indicator and will be destroyed after the initial load.
67902 * Create a new LoadMask
67903 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
67904 * @param {Object} config The config object
67906 Roo.LoadMask = function(el, config){
67907 this.el = Roo.get(el);
67908 Roo.apply(this, config);
67910 this.store.on('beforeload', this.onBeforeLoad, this);
67911 this.store.on('load', this.onLoad, this);
67912 this.store.on('loadexception', this.onLoadException, this);
67913 this.removeMask = false;
67915 var um = this.el.getUpdateManager();
67916 um.showLoadIndicator = false; // disable the default indicator
67917 um.on('beforeupdate', this.onBeforeLoad, this);
67918 um.on('update', this.onLoad, this);
67919 um.on('failure', this.onLoad, this);
67920 this.removeMask = true;
67924 Roo.LoadMask.prototype = {
67926 * @cfg {Boolean} removeMask
67927 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
67928 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
67930 removeMask : false,
67932 * @cfg {String} msg
67933 * The text to display in a centered loading message box (defaults to 'Loading...')
67935 msg : 'Loading...',
67937 * @cfg {String} msgCls
67938 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
67940 msgCls : 'x-mask-loading',
67943 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
67949 * Disables the mask to prevent it from being displayed
67951 disable : function(){
67952 this.disabled = true;
67956 * Enables the mask so that it can be displayed
67958 enable : function(){
67959 this.disabled = false;
67962 onLoadException : function()
67964 Roo.log(arguments);
67966 if (typeof(arguments[3]) != 'undefined') {
67967 Roo.MessageBox.alert("Error loading",arguments[3]);
67971 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
67972 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
67979 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67982 onLoad : function()
67984 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67988 onBeforeLoad : function(){
67989 if(!this.disabled){
67990 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
67995 destroy : function(){
67997 this.store.un('beforeload', this.onBeforeLoad, this);
67998 this.store.un('load', this.onLoad, this);
67999 this.store.un('loadexception', this.onLoadException, this);
68001 var um = this.el.getUpdateManager();
68002 um.un('beforeupdate', this.onBeforeLoad, this);
68003 um.un('update', this.onLoad, this);
68004 um.un('failure', this.onLoad, this);
68009 * Ext JS Library 1.1.1
68010 * Copyright(c) 2006-2007, Ext JS, LLC.
68012 * Originally Released Under LGPL - original licence link has changed is not relivant.
68015 * <script type="text/javascript">
68020 * @class Roo.XTemplate
68021 * @extends Roo.Template
68022 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
68024 var t = new Roo.XTemplate(
68025 '<select name="{name}">',
68026 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
68030 // then append, applying the master template values
68033 * Supported features:
68038 {a_variable} - output encoded.
68039 {a_variable.format:("Y-m-d")} - call a method on the variable
68040 {a_variable:raw} - unencoded output
68041 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
68042 {a_variable:this.method_on_template(...)} - call a method on the template object.
68047 <tpl for="a_variable or condition.."></tpl>
68048 <tpl if="a_variable or condition"></tpl>
68049 <tpl exec="some javascript"></tpl>
68050 <tpl name="named_template"></tpl> (experimental)
68052 <tpl for="."></tpl> - just iterate the property..
68053 <tpl for=".."></tpl> - iterates with the parent (probably the template)
68057 Roo.XTemplate = function()
68059 Roo.XTemplate.superclass.constructor.apply(this, arguments);
68066 Roo.extend(Roo.XTemplate, Roo.Template, {
68069 * The various sub templates
68074 * basic tag replacing syntax
68077 * // you can fake an object call by doing this
68081 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
68084 * compile the template
68086 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
68089 compile: function()
68093 s = ['<tpl>', s, '</tpl>'].join('');
68095 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
68096 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
68097 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
68098 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
68099 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
68104 while(true == !!(m = s.match(re))){
68105 var forMatch = m[0].match(nameRe),
68106 ifMatch = m[0].match(ifRe),
68107 execMatch = m[0].match(execRe),
68108 namedMatch = m[0].match(namedRe),
68113 name = forMatch && forMatch[1] ? forMatch[1] : '';
68116 // if - puts fn into test..
68117 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
68119 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
68124 // exec - calls a function... returns empty if true is returned.
68125 exp = execMatch && execMatch[1] ? execMatch[1] : null;
68127 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
68135 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
68136 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
68137 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
68140 var uid = namedMatch ? namedMatch[1] : id;
68144 id: namedMatch ? namedMatch[1] : id,
68151 s = s.replace(m[0], '');
68153 s = s.replace(m[0], '{xtpl'+ id + '}');
68158 for(var i = tpls.length-1; i >= 0; --i){
68159 this.compileTpl(tpls[i]);
68160 this.tpls[tpls[i].id] = tpls[i];
68162 this.master = tpls[tpls.length-1];
68166 * same as applyTemplate, except it's done to one of the subTemplates
68167 * when using named templates, you can do:
68169 * var str = pl.applySubTemplate('your-name', values);
68172 * @param {Number} id of the template
68173 * @param {Object} values to apply to template
68174 * @param {Object} parent (normaly the instance of this object)
68176 applySubTemplate : function(id, values, parent)
68180 var t = this.tpls[id];
68184 if(t.test && !t.test.call(this, values, parent)){
68188 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
68189 Roo.log(e.toString());
68195 if(t.exec && t.exec.call(this, values, parent)){
68199 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
68200 Roo.log(e.toString());
68205 var vs = t.target ? t.target.call(this, values, parent) : values;
68206 parent = t.target ? values : parent;
68207 if(t.target && vs instanceof Array){
68209 for(var i = 0, len = vs.length; i < len; i++){
68210 buf[buf.length] = t.compiled.call(this, vs[i], parent);
68212 return buf.join('');
68214 return t.compiled.call(this, vs, parent);
68216 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
68217 Roo.log(e.toString());
68218 Roo.log(t.compiled);
68223 compileTpl : function(tpl)
68225 var fm = Roo.util.Format;
68226 var useF = this.disableFormats !== true;
68227 var sep = Roo.isGecko ? "+" : ",";
68228 var undef = function(str) {
68229 Roo.log("Property not found :" + str);
68233 var fn = function(m, name, format, args)
68235 //Roo.log(arguments);
68236 args = args ? args.replace(/\\'/g,"'") : args;
68237 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
68238 if (typeof(format) == 'undefined') {
68239 format= 'htmlEncode';
68241 if (format == 'raw' ) {
68245 if(name.substr(0, 4) == 'xtpl'){
68246 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
68249 // build an array of options to determine if value is undefined..
68251 // basically get 'xxxx.yyyy' then do
68252 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
68253 // (function () { Roo.log("Property not found"); return ''; })() :
68258 Roo.each(name.split('.'), function(st) {
68259 lookfor += (lookfor.length ? '.': '') + st;
68260 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
68263 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
68266 if(format && useF){
68268 args = args ? ',' + args : "";
68270 if(format.substr(0, 5) != "this."){
68271 format = "fm." + format + '(';
68273 format = 'this.call("'+ format.substr(5) + '", ';
68277 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
68281 // called with xxyx.yuu:(test,test)
68283 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
68285 // raw.. - :raw modifier..
68286 return "'"+ sep + udef_st + name + ")"+sep+"'";
68290 // branched to use + in gecko and [].join() in others
68292 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
68293 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
68296 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
68297 body.push(tpl.body.replace(/(\r\n|\n)/g,
68298 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
68299 body.push("'].join('');};};");
68300 body = body.join('');
68303 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
68305 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
68311 applyTemplate : function(values){
68312 return this.master.compiled.call(this, values, {});
68313 //var s = this.subs;
68316 apply : function(){
68317 return this.applyTemplate.apply(this, arguments);
68322 Roo.XTemplate.from = function(el){
68323 el = Roo.getDom(el);
68324 return new Roo.XTemplate(el.value || el.innerHTML);
68331 * @class Roo.dialog.UploadCropbox
68332 * @extends Roo.BoxComponent
68333 * Dialog UploadCropbox class
68334 * @cfg {String} emptyText show when image has been loaded
68335 * @cfg {String} rotateNotify show when image too small to rotate
68336 * @cfg {Number} errorTimeout default 3000
68337 * @cfg {Number} minWidth default 300
68338 * @cfg {Number} minHeight default 300
68339 * @cfg {Number} outputMaxWidth default 1200
68340 * @cfg {Number} windowSize default 300
68341 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
68342 * @cfg {Boolean} isDocument (true|false) default false
68343 * @cfg {String} url action url
68344 * @cfg {String} paramName default 'imageUpload'
68345 * @cfg {String} method default POST
68346 * @cfg {Boolean} loadMask (true|false) default true
68347 * @cfg {Boolean} loadingText default 'Loading...'
68350 * Create a new UploadCropbox
68351 * @param {Object} config The config object
68354 Roo.dialog.UploadCropbox = function(config){
68355 Roo.dialog.UploadCropbox.superclass.constructor.call(this, config);
68359 * @event beforeselectfile
68360 * Fire before select file
68361 * @param {Roo.dialog.UploadCropbox} this
68363 "beforeselectfile" : true,
68366 * Fire after initEvent
68367 * @param {Roo.dialog.UploadCropbox} this
68372 * Fire after initEvent
68373 * @param {Roo.dialog.UploadCropbox} this
68374 * @param {String} data
68379 * Fire when preparing the file data
68380 * @param {Roo.dialog.UploadCropbox} this
68381 * @param {Object} file
68386 * Fire when get exception
68387 * @param {Roo.dialog.UploadCropbox} this
68388 * @param {XMLHttpRequest} xhr
68390 "exception" : true,
68392 * @event beforeloadcanvas
68393 * Fire before load the canvas
68394 * @param {Roo.dialog.UploadCropbox} this
68395 * @param {String} src
68397 "beforeloadcanvas" : true,
68400 * Fire when trash image
68401 * @param {Roo.dialog.UploadCropbox} this
68406 * Fire when download the image
68407 * @param {Roo.dialog.UploadCropbox} this
68411 * @event footerbuttonclick
68412 * Fire when footerbuttonclick
68413 * @param {Roo.dialog.UploadCropbox} this
68414 * @param {String} type
68416 "footerbuttonclick" : true,
68420 * @param {Roo.dialog.UploadCropbox} this
68425 * Fire when rotate the image
68426 * @param {Roo.dialog.UploadCropbox} this
68427 * @param {String} pos
68432 * Fire when inspect the file
68433 * @param {Roo.dialog.UploadCropbox} this
68434 * @param {Object} file
68439 * Fire when xhr upload the file
68440 * @param {Roo.dialog.UploadCropbox} this
68441 * @param {Object} data
68446 * Fire when arrange the file data
68447 * @param {Roo.dialog.UploadCropbox} this
68448 * @param {Object} formData
68452 * @event loadcanvas
68453 * Fire after load the canvas
68454 * @param {Roo.dialog.UploadCropbox}
68455 * @param {Object} imgEl
68457 "loadcanvas" : true
68460 this.buttons = this.buttons || Roo.dialog.UploadCropbox.footer.STANDARD;
68463 Roo.extend(Roo.dialog.UploadCropbox, Roo.Component, {
68465 emptyText : 'Click to upload image',
68466 rotateNotify : 'Image is too small to rotate',
68467 errorTimeout : 3000,
68478 outputMaxWidth : 1200,
68483 cropType : 'image/jpeg',
68485 canvasLoaded : false,
68486 isDocument : false,
68488 paramName : 'imageUpload',
68490 loadingText : 'Loading...',
68493 getAutoCreate : function()
68497 cls : 'roo-upload-cropbox',
68501 cls : 'roo-upload-cropbox-selector',
68506 cls : 'roo-upload-cropbox-body',
68507 style : 'cursor:pointer',
68511 cls : 'roo-upload-cropbox-preview'
68515 cls : 'roo-upload-cropbox-thumb'
68519 cls : 'roo-upload-cropbox-empty-notify',
68520 html : this.emptyText
68524 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
68525 html : this.rotateNotify
68531 cls : 'roo-upload-cropbox-footer',
68534 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
68544 onRender : function(ct, position)
68546 Roo.dialog.UploadCropbox.superclass.onRender.call(this, ct, position);
68549 if (this.el.attr('xtype')) {
68550 this.el.attr('xtypex', this.el.attr('xtype'));
68551 this.el.dom.removeAttribute('xtype');
68557 var cfg = Roo.apply({}, this.getAutoCreate());
68559 cfg.id = this.id || Roo.id();
68562 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
68565 if (this.style) { // fixme needs to support more complex style data.
68566 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
68569 this.el = ct.createChild(cfg, position);
68574 if (this.buttons.length) {
68576 Roo.each(this.buttons, function(bb) {
68578 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
68580 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
68586 this.maskEl = this.el;
68590 initEvents : function()
68592 this.urlAPI = (window.createObjectURL && window) ||
68593 (window.URL && URL.revokeObjectURL && URL) ||
68594 (window.webkitURL && webkitURL);
68596 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
68597 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68599 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
68600 this.selectorEl.hide();
68602 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
68603 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68605 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
68606 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68607 this.thumbEl.hide();
68609 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
68610 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68612 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
68613 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68614 this.errorEl.hide();
68616 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
68617 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68618 this.footerEl.hide();
68620 this.setThumbBoxSize();
68626 this.fireEvent('initial', this);
68633 window.addEventListener("resize", function() { _this.resize(); } );
68635 this.bodyEl.on('click', this.beforeSelectFile, this);
68638 this.bodyEl.on('touchstart', this.onTouchStart, this);
68639 this.bodyEl.on('touchmove', this.onTouchMove, this);
68640 this.bodyEl.on('touchend', this.onTouchEnd, this);
68644 this.bodyEl.on('mousedown', this.onMouseDown, this);
68645 this.bodyEl.on('mousemove', this.onMouseMove, this);
68646 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
68647 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
68648 Roo.get(document).on('mouseup', this.onMouseUp, this);
68651 this.selectorEl.on('change', this.onFileSelected, this);
68657 this.baseScale = 1;
68659 this.baseRotate = 1;
68660 this.dragable = false;
68661 this.pinching = false;
68664 this.cropData = false;
68665 this.notifyEl.dom.innerHTML = this.emptyText;
68667 // this.selectorEl.dom.value = '';
68671 resize : function()
68673 if(this.fireEvent('resize', this) != false){
68674 this.setThumbBoxPosition();
68675 this.setCanvasPosition();
68679 onFooterButtonClick : function(e, el, o, type)
68682 case 'rotate-left' :
68683 this.onRotateLeft(e);
68685 case 'rotate-right' :
68686 this.onRotateRight(e);
68689 this.beforeSelectFile(e);
68707 this.fireEvent('footerbuttonclick', this, type);
68710 beforeSelectFile : function(e)
68712 e.preventDefault();
68714 if(this.fireEvent('beforeselectfile', this) != false){
68715 this.selectorEl.dom.click();
68719 onFileSelected : function(e)
68721 e.preventDefault();
68723 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
68727 var file = this.selectorEl.dom.files[0];
68729 if(this.fireEvent('inspect', this, file) != false){
68730 this.prepare(file);
68735 trash : function(e)
68737 this.fireEvent('trash', this);
68740 download : function(e)
68742 this.fireEvent('download', this);
68745 center : function(e)
68747 this.setCanvasPosition();
68750 loadCanvas : function(src)
68752 if(this.fireEvent('beforeloadcanvas', this, src) != false){
68756 this.imageEl = document.createElement('img');
68760 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
68762 this.imageEl.src = src;
68766 onLoadCanvas : function()
68768 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
68769 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
68771 if(this.fireEvent('loadcanvas', this, this.imageEl) != false){
68773 this.bodyEl.un('click', this.beforeSelectFile, this);
68775 this.notifyEl.hide();
68776 this.thumbEl.show();
68777 this.footerEl.show();
68779 this.baseRotateLevel();
68781 if(this.isDocument){
68782 this.setThumbBoxSize();
68785 this.setThumbBoxPosition();
68787 this.baseScaleLevel();
68793 this.canvasLoaded = true;
68798 this.maskEl.unmask();
68803 setCanvasPosition : function(center = true)
68805 if(!this.canvasEl){
68809 var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
68810 var newCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
68813 this.previewEl.setLeft(newCenterLeft);
68814 this.previewEl.setTop(newCenterTop);
68819 var oldScaleLevel = this.baseScale * Math.pow(1.02, this.startScale);
68820 var oldCanvasWidth = Math.floor(this.imageEl.OriginWidth * oldScaleLevel);
68821 var oldCanvasHeight = Math.floor(this.imageEl.OriginHeight * oldScaleLevel);
68823 var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - oldCanvasWidth) / 2);
68824 var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - oldCanvasHeight) / 2);
68826 var leftDiff = newCenterLeft - oldCenterLeft;
68827 var topDiff = newCenterTop - oldCenterTop;
68829 var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
68830 var newPreviewTop = this.previewEl.getTop(true) + topDiff;
68832 this.previewEl.setLeft(newPreviewLeft);
68833 this.previewEl.setTop(newPreviewTop);
68837 onMouseDown : function(e)
68841 this.dragable = true;
68842 this.pinching = false;
68844 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
68845 this.dragable = false;
68849 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
68850 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
68854 onMouseMove : function(e)
68858 if(!this.canvasLoaded){
68862 if (!this.dragable){
68866 var maxPaddingLeft = this.canvasEl.width / 0.9 * 0.05;
68867 var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
68869 if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
68870 maxPaddingLeft = (this.canvasEl.height * this.minWidth / this.minHeight - this.canvasEl.width) / 2 + maxPaddingLeft;
68873 if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
68874 maxPaddingTop = (this.canvasEl.width * this.minHeight / this.minWidth - this.canvasEl.height) / 2 + maxPaddingTop;
68877 var minX = Math.ceil(this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - this.canvasEl.width - maxPaddingLeft);
68878 var minY = Math.ceil(this.thumbEl.getTop(true) + this.thumbEl.getHeight() - this.canvasEl.height - maxPaddingTop);
68880 var maxX = Math.ceil(this.thumbEl.getLeft(true) + maxPaddingLeft);
68881 var maxY = Math.ceil(this.thumbEl.getTop(true) + maxPaddingTop);
68895 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
68896 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
68898 x = x - this.mouseX;
68899 y = y - this.mouseY;
68901 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
68902 var bgY = Math.ceil(y + this.previewEl.getTop(true));
68904 bgX = (bgX < minX) ? minX : ((bgX > maxX) ? maxX : bgX);
68905 bgY = (bgY < minY) ? minY : ((bgY > maxY) ? maxY : bgY);
68907 this.previewEl.setLeft(bgX);
68908 this.previewEl.setTop(bgY);
68910 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
68911 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
68914 onMouseUp : function(e)
68918 this.dragable = false;
68921 onMouseWheel : function(e)
68925 this.startScale = this.scale;
68926 this.scale = (e.getWheelDelta() > 0) ? (this.scale + 1) : (this.scale - 1);
68928 if(!this.zoomable()){
68929 this.scale = this.startScale;
68939 zoomable : function()
68941 var minScale = this.thumbEl.getWidth() / this.minWidth;
68943 if(this.minWidth < this.minHeight){
68944 minScale = this.thumbEl.getHeight() / this.minHeight;
68947 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
68948 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
68950 var maxWidth = this.imageEl.OriginWidth;
68951 var maxHeight = this.imageEl.OriginHeight;
68954 var newCanvasWidth = Math.floor(this.imageEl.OriginWidth * this.getScaleLevel());
68955 var newCanvasHeight = Math.floor(this.imageEl.OriginHeight * this.getScaleLevel());
68957 var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
68958 var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
68960 var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - newCanvasWidth) / 2);
68961 var newCenterTop = Math.ceil((this.bodyEl.getHeight() - newCanvasHeight) / 2);
68963 var leftDiff = newCenterLeft - oldCenterLeft;
68964 var topDiff = newCenterTop - oldCenterTop;
68966 var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
68967 var newPreviewTop = this.previewEl.getTop(true) + topDiff;
68969 var paddingLeft = newPreviewLeft - this.thumbEl.getLeft(true);
68970 var paddingTop = newPreviewTop - this.thumbEl.getTop(true);
68972 var paddingRight = this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - newCanvasWidth - newPreviewLeft;
68973 var paddingBottom = this.thumbEl.getTop(true) + this.thumbEl.getHeight() - newCanvasHeight - newPreviewTop;
68975 var maxPaddingLeft = newCanvasWidth / 0.9 * 0.05;
68976 var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
68978 if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
68979 maxPaddingLeft = (newCanvasHeight * this.minWidth / this.minHeight - newCanvasWidth) / 2 + maxPaddingLeft;
68982 if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
68983 maxPaddingTop = (newCanvasWidth * this.minHeight / this.minWidth - newCanvasHeight) / 2 + maxPaddingTop;
68988 (this.rotate == 0 || this.rotate == 180) &&
68990 width > this.imageEl.OriginWidth ||
68991 height > this.imageEl.OriginHeight ||
68992 (width < this.minWidth && height < this.minHeight)
69000 (this.rotate == 90 || this.rotate == 270) &&
69002 width > this.imageEl.OriginWidth ||
69003 height > this.imageEl.OriginHeight ||
69004 (width < this.minHeight && height < this.minWidth)
69011 !this.isDocument &&
69012 (this.rotate == 0 || this.rotate == 180) &&
69015 paddingLeft > maxPaddingLeft ||
69016 paddingRight > maxPaddingLeft ||
69017 paddingTop > maxPaddingTop ||
69018 paddingBottom > maxPaddingTop ||
69020 width > maxWidth ||
69028 !this.isDocument &&
69029 (this.rotate == 90 || this.rotate == 270) &&
69031 width < this.minHeight ||
69032 width > this.imageEl.OriginWidth ||
69033 height < this.minWidth ||
69034 height > this.imageEl.OriginHeight
69044 onRotateLeft : function(e)
69046 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
69048 var minScale = this.thumbEl.getWidth() / this.minWidth;
69050 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
69051 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
69053 this.startScale = this.scale;
69055 while (this.getScaleLevel() < minScale){
69057 this.scale = this.scale + 1;
69059 if(!this.zoomable()){
69064 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
69065 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
69070 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
69077 this.scale = this.startScale;
69079 this.onRotateFail();
69084 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
69086 if(this.isDocument){
69087 this.setThumbBoxSize();
69088 this.setThumbBoxPosition();
69089 this.setCanvasPosition();
69094 this.fireEvent('rotate', this, 'left');
69098 onRotateRight : function(e)
69100 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
69102 var minScale = this.thumbEl.getWidth() / this.minWidth;
69104 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
69105 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
69107 this.startScale = this.scale;
69109 while (this.getScaleLevel() < minScale){
69111 this.scale = this.scale + 1;
69113 if(!this.zoomable()){
69118 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
69119 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
69124 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
69131 this.scale = this.startScale;
69133 this.onRotateFail();
69138 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
69140 if(this.isDocument){
69141 this.setThumbBoxSize();
69142 this.setThumbBoxPosition();
69143 this.setCanvasPosition();
69148 this.fireEvent('rotate', this, 'right');
69151 onRotateFail : function()
69153 this.errorEl.show(true);
69157 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
69162 this.previewEl.dom.innerHTML = '';
69164 var canvasEl = document.createElement("canvas");
69166 var contextEl = canvasEl.getContext("2d");
69168 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69169 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69170 var center = this.imageEl.OriginWidth / 2;
69172 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
69173 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69174 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69175 center = this.imageEl.OriginHeight / 2;
69178 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
69180 contextEl.translate(center, center);
69181 contextEl.rotate(this.rotate * Math.PI / 180);
69183 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
69185 this.canvasEl = document.createElement("canvas");
69187 this.contextEl = this.canvasEl.getContext("2d");
69189 switch (this.rotate) {
69192 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69193 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69195 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69200 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69201 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69203 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69204 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);
69208 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69213 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69214 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69216 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69217 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);
69221 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);
69226 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69227 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69229 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69230 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69234 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);
69241 this.previewEl.appendChild(this.canvasEl);
69243 this.setCanvasPosition(false);
69248 if(!this.canvasLoaded){
69252 var imageCanvas = document.createElement("canvas");
69254 var imageContext = imageCanvas.getContext("2d");
69256 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
69257 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
69259 var center = imageCanvas.width / 2;
69261 imageContext.translate(center, center);
69263 imageContext.rotate(this.rotate * Math.PI / 180);
69265 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
69267 var canvas = document.createElement("canvas");
69269 var context = canvas.getContext("2d");
69271 canvas.width = this.thumbEl.getWidth() / this.getScaleLevel();
69273 canvas.height = this.thumbEl.getHeight() / this.getScaleLevel();
69275 switch (this.rotate) {
69278 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
69279 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
69281 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69282 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69284 var sx = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
69285 var sy = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
69287 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69288 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69290 if(canvas.width > this.outputMaxWidth) {
69291 var scale = this.outputMaxWidth / canvas.width;
69292 canvas.width = canvas.width * scale;
69293 canvas.height = canvas.height * scale;
69294 context.scale(scale, scale);
69297 context.fillStyle = 'white';
69298 context.fillRect(0, 0, this.thumbEl.getWidth() / this.getScaleLevel(), this.thumbEl.getHeight() / this.getScaleLevel());
69301 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69306 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
69307 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
69309 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69310 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69312 var targetWidth = this.minWidth - 2 * x;
69313 var targetHeight = this.minHeight - 2 * y;
69317 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69318 scale = targetWidth / width;
69321 if(x > 0 && y == 0){
69322 scale = targetHeight / height;
69325 if(x > 0 && y > 0){
69326 scale = targetWidth / width;
69328 if(width < height){
69329 scale = targetHeight / height;
69333 context.scale(scale, scale);
69335 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69336 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69338 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69339 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69341 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
69343 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69348 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
69349 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
69351 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69352 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69354 var targetWidth = this.minWidth - 2 * x;
69355 var targetHeight = this.minHeight - 2 * y;
69359 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69360 scale = targetWidth / width;
69363 if(x > 0 && y == 0){
69364 scale = targetHeight / height;
69367 if(x > 0 && y > 0){
69368 scale = targetWidth / width;
69370 if(width < height){
69371 scale = targetHeight / height;
69375 context.scale(scale, scale);
69377 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69378 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69380 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69381 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69383 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
69384 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
69386 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69391 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
69392 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
69394 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69395 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69397 var targetWidth = this.minWidth - 2 * x;
69398 var targetHeight = this.minHeight - 2 * y;
69402 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69403 scale = targetWidth / width;
69406 if(x > 0 && y == 0){
69407 scale = targetHeight / height;
69410 if(x > 0 && y > 0){
69411 scale = targetWidth / width;
69413 if(width < height){
69414 scale = targetHeight / height;
69418 context.scale(scale, scale);
69419 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69420 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69422 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69423 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69425 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
69427 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69434 this.cropData = canvas.toDataURL(this.cropType);
69436 if(this.fireEvent('crop', this, this.cropData) !== false){
69437 this.process(this.file, this.cropData);
69444 setThumbBoxSize : function()
69448 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
69449 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
69450 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
69452 this.minWidth = width;
69453 this.minHeight = height;
69455 if(this.rotate == 90 || this.rotate == 270){
69456 this.minWidth = height;
69457 this.minHeight = width;
69461 height = this.windowSize;
69462 width = Math.ceil(this.minWidth * height / this.minHeight);
69464 if(this.minWidth > this.minHeight){
69465 width = this.windowSize;
69466 height = Math.ceil(this.minHeight * width / this.minWidth);
69469 this.thumbEl.setStyle({
69470 width : width + 'px',
69471 height : height + 'px'
69478 setThumbBoxPosition : function()
69480 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
69481 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
69483 this.thumbEl.setLeft(x);
69484 this.thumbEl.setTop(y);
69488 baseRotateLevel : function()
69490 this.baseRotate = 1;
69493 typeof(this.exif) != 'undefined' &&
69494 typeof(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
69495 [1, 3, 6, 8].indexOf(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != -1
69497 this.baseRotate = this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']];
69500 this.rotate = Roo.dialog.UploadCropbox['Orientation'][this.baseRotate];
69504 baseScaleLevel : function()
69508 if(this.isDocument){
69510 if(this.baseRotate == 6 || this.baseRotate == 8){
69512 height = this.thumbEl.getHeight();
69513 this.baseScale = height / this.imageEl.OriginWidth;
69515 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
69516 width = this.thumbEl.getWidth();
69517 this.baseScale = width / this.imageEl.OriginHeight;
69523 height = this.thumbEl.getHeight();
69524 this.baseScale = height / this.imageEl.OriginHeight;
69526 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
69527 width = this.thumbEl.getWidth();
69528 this.baseScale = width / this.imageEl.OriginWidth;
69534 if(this.baseRotate == 6 || this.baseRotate == 8){
69536 width = this.thumbEl.getHeight();
69537 this.baseScale = width / this.imageEl.OriginHeight;
69539 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
69540 height = this.thumbEl.getWidth();
69541 this.baseScale = height / this.imageEl.OriginHeight;
69544 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69545 height = this.thumbEl.getWidth();
69546 this.baseScale = height / this.imageEl.OriginHeight;
69548 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
69549 width = this.thumbEl.getHeight();
69550 this.baseScale = width / this.imageEl.OriginWidth;
69557 width = this.thumbEl.getWidth();
69558 this.baseScale = width / this.imageEl.OriginWidth;
69560 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
69561 height = this.thumbEl.getHeight();
69562 this.baseScale = height / this.imageEl.OriginHeight;
69565 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69567 height = this.thumbEl.getHeight();
69568 this.baseScale = height / this.imageEl.OriginHeight;
69570 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
69571 width = this.thumbEl.getWidth();
69572 this.baseScale = width / this.imageEl.OriginWidth;
69577 if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight) {
69578 this.baseScale = width / this.minWidth;
69584 getScaleLevel : function()
69586 return this.baseScale * Math.pow(1.02, this.scale);
69589 onTouchStart : function(e)
69591 if(!this.canvasLoaded){
69592 this.beforeSelectFile(e);
69596 var touches = e.browserEvent.touches;
69602 if(touches.length == 1){
69603 this.onMouseDown(e);
69607 if(touches.length != 2){
69613 for(var i = 0, finger; finger = touches[i]; i++){
69614 coords.push(finger.pageX, finger.pageY);
69617 var x = Math.pow(coords[0] - coords[2], 2);
69618 var y = Math.pow(coords[1] - coords[3], 2);
69620 this.startDistance = Math.sqrt(x + y);
69622 this.startScale = this.scale;
69624 this.pinching = true;
69625 this.dragable = false;
69629 onTouchMove : function(e)
69631 if(!this.pinching && !this.dragable){
69635 var touches = e.browserEvent.touches;
69642 this.onMouseMove(e);
69648 for(var i = 0, finger; finger = touches[i]; i++){
69649 coords.push(finger.pageX, finger.pageY);
69652 var x = Math.pow(coords[0] - coords[2], 2);
69653 var y = Math.pow(coords[1] - coords[3], 2);
69655 this.endDistance = Math.sqrt(x + y);
69657 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
69659 if(!this.zoomable()){
69660 this.scale = this.startScale;
69668 onTouchEnd : function(e)
69670 this.pinching = false;
69671 this.dragable = false;
69675 process : function(file, crop)
69678 this.maskEl.mask(this.loadingText);
69681 this.xhr = new XMLHttpRequest();
69683 file.xhr = this.xhr;
69685 this.xhr.open(this.method, this.url, true);
69688 "Accept": "application/json",
69689 "Cache-Control": "no-cache",
69690 "X-Requested-With": "XMLHttpRequest"
69693 for (var headerName in headers) {
69694 var headerValue = headers[headerName];
69696 this.xhr.setRequestHeader(headerName, headerValue);
69702 this.xhr.onload = function()
69704 _this.xhrOnLoad(_this.xhr);
69707 this.xhr.onerror = function()
69709 _this.xhrOnError(_this.xhr);
69712 var formData = new FormData();
69714 formData.append('returnHTML', 'NO');
69717 formData.append('crop', crop);
69718 var blobBin = atob(crop.split(',')[1]);
69720 for(var i = 0; i < blobBin.length; i++) {
69721 array.push(blobBin.charCodeAt(i));
69723 var croppedFile =new Blob([new Uint8Array(array)], {type: this.cropType});
69724 formData.append(this.paramName, croppedFile, file.name);
69727 if(typeof(file.filename) != 'undefined'){
69728 formData.append('filename', file.filename);
69731 if(typeof(file.mimetype) != 'undefined'){
69732 formData.append('mimetype', file.mimetype);
69735 if(this.fireEvent('arrange', this, formData) != false){
69736 this.xhr.send(formData);
69740 xhrOnLoad : function(xhr)
69743 this.maskEl.unmask();
69746 if (xhr.readyState !== 4) {
69747 this.fireEvent('exception', this, xhr);
69751 var response = Roo.decode(xhr.responseText);
69753 if(!response.success){
69754 this.fireEvent('exception', this, xhr);
69758 var response = Roo.decode(xhr.responseText);
69760 this.fireEvent('upload', this, response);
69764 xhrOnError : function()
69767 this.maskEl.unmask();
69770 Roo.log('xhr on error');
69772 var response = Roo.decode(xhr.responseText);
69778 prepare : function(file)
69781 this.maskEl.mask(this.loadingText);
69787 if(typeof(file) === 'string'){
69788 this.loadCanvas(file);
69792 if(!file || !this.urlAPI){
69797 if(typeof(file.type) != 'undefined' && file.type.length != 0) {
69798 this.cropType = file.type;
69803 if(this.fireEvent('prepare', this, this.file) != false){
69805 var reader = new FileReader();
69807 reader.onload = function (e) {
69808 if (e.target.error) {
69809 Roo.log(e.target.error);
69813 var buffer = e.target.result,
69814 dataView = new DataView(buffer),
69816 maxOffset = dataView.byteLength - 4,
69820 if (dataView.getUint16(0) === 0xffd8) {
69821 while (offset < maxOffset) {
69822 markerBytes = dataView.getUint16(offset);
69824 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
69825 markerLength = dataView.getUint16(offset + 2) + 2;
69826 if (offset + markerLength > dataView.byteLength) {
69827 Roo.log('Invalid meta data: Invalid segment size.');
69831 if(markerBytes == 0xffe1){
69832 _this.parseExifData(
69839 offset += markerLength;
69849 var url = _this.urlAPI.createObjectURL(_this.file);
69851 _this.loadCanvas(url);
69856 reader.readAsArrayBuffer(this.file);
69862 parseExifData : function(dataView, offset, length)
69864 var tiffOffset = offset + 10,
69868 if (dataView.getUint32(offset + 4) !== 0x45786966) {
69869 // No Exif data, might be XMP data instead
69873 // Check for the ASCII code for "Exif" (0x45786966):
69874 if (dataView.getUint32(offset + 4) !== 0x45786966) {
69875 // No Exif data, might be XMP data instead
69878 if (tiffOffset + 8 > dataView.byteLength) {
69879 Roo.log('Invalid Exif data: Invalid segment size.');
69882 // Check for the two null bytes:
69883 if (dataView.getUint16(offset + 8) !== 0x0000) {
69884 Roo.log('Invalid Exif data: Missing byte alignment offset.');
69887 // Check the byte alignment:
69888 switch (dataView.getUint16(tiffOffset)) {
69890 littleEndian = true;
69893 littleEndian = false;
69896 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
69899 // Check for the TIFF tag marker (0x002A):
69900 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
69901 Roo.log('Invalid Exif data: Missing TIFF marker.');
69904 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
69905 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
69907 this.parseExifTags(
69910 tiffOffset + dirOffset,
69915 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
69920 if (dirOffset + 6 > dataView.byteLength) {
69921 Roo.log('Invalid Exif data: Invalid directory offset.');
69924 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
69925 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
69926 if (dirEndOffset + 4 > dataView.byteLength) {
69927 Roo.log('Invalid Exif data: Invalid directory size.');
69930 for (i = 0; i < tagsNumber; i += 1) {
69934 dirOffset + 2 + 12 * i, // tag offset
69938 // Return the offset to the next directory:
69939 return dataView.getUint32(dirEndOffset, littleEndian);
69942 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
69944 var tag = dataView.getUint16(offset, littleEndian);
69946 this.exif[tag] = this.getExifValue(
69950 dataView.getUint16(offset + 2, littleEndian), // tag type
69951 dataView.getUint32(offset + 4, littleEndian), // tag length
69956 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
69958 var tagType = Roo.dialog.UploadCropbox.exifTagTypes[type],
69967 Roo.log('Invalid Exif data: Invalid tag type.');
69971 tagSize = tagType.size * length;
69972 // Determine if the value is contained in the dataOffset bytes,
69973 // or if the value at the dataOffset is a pointer to the actual data:
69974 dataOffset = tagSize > 4 ?
69975 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
69976 if (dataOffset + tagSize > dataView.byteLength) {
69977 Roo.log('Invalid Exif data: Invalid data offset.');
69980 if (length === 1) {
69981 return tagType.getValue(dataView, dataOffset, littleEndian);
69984 for (i = 0; i < length; i += 1) {
69985 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
69988 if (tagType.ascii) {
69990 // Concatenate the chars:
69991 for (i = 0; i < values.length; i += 1) {
69993 // Ignore the terminating NULL byte(s):
69994 if (c === '\u0000') {
70006 Roo.apply(Roo.dialog.UploadCropbox, {
70008 'Orientation': 0x0112
70012 1: 0, //'top-left',
70014 3: 180, //'bottom-right',
70015 // 4: 'bottom-left',
70017 6: 90, //'right-top',
70018 // 7: 'right-bottom',
70019 8: 270 //'left-bottom'
70023 // byte, 8-bit unsigned int:
70025 getValue: function (dataView, dataOffset) {
70026 return dataView.getUint8(dataOffset);
70030 // ascii, 8-bit byte:
70032 getValue: function (dataView, dataOffset) {
70033 return String.fromCharCode(dataView.getUint8(dataOffset));
70038 // short, 16 bit int:
70040 getValue: function (dataView, dataOffset, littleEndian) {
70041 return dataView.getUint16(dataOffset, littleEndian);
70045 // long, 32 bit int:
70047 getValue: function (dataView, dataOffset, littleEndian) {
70048 return dataView.getUint32(dataOffset, littleEndian);
70052 // rational = two long values, first is numerator, second is denominator:
70054 getValue: function (dataView, dataOffset, littleEndian) {
70055 return dataView.getUint32(dataOffset, littleEndian) /
70056 dataView.getUint32(dataOffset + 4, littleEndian);
70060 // slong, 32 bit signed int:
70062 getValue: function (dataView, dataOffset, littleEndian) {
70063 return dataView.getInt32(dataOffset, littleEndian);
70067 // srational, two slongs, first is numerator, second is denominator:
70069 getValue: function (dataView, dataOffset, littleEndian) {
70070 return dataView.getInt32(dataOffset, littleEndian) /
70071 dataView.getInt32(dataOffset + 4, littleEndian);
70081 cls : 'btn-group roo-upload-cropbox-rotate-left',
70082 action : 'rotate-left',
70086 cls : 'btn btn-default',
70087 html : '<i class="fa fa-undo"></i>'
70093 cls : 'btn-group roo-upload-cropbox-picture',
70094 action : 'picture',
70098 cls : 'btn btn-default',
70099 html : '<i class="fa fa-picture-o"></i>'
70105 cls : 'btn-group roo-upload-cropbox-rotate-right',
70106 action : 'rotate-right',
70110 cls : 'btn btn-default',
70111 html : '<i class="fa fa-repeat"></i>'
70119 cls : 'btn-group roo-upload-cropbox-rotate-left',
70120 action : 'rotate-left',
70124 cls : 'btn btn-default',
70125 html : '<i class="fa fa-undo"></i>'
70131 cls : 'btn-group roo-upload-cropbox-download',
70132 action : 'download',
70136 cls : 'btn btn-default',
70137 html : '<i class="fa fa-download"></i>'
70143 cls : 'btn-group roo-upload-cropbox-crop',
70148 cls : 'btn btn-default',
70149 html : '<i class="fa fa-crop"></i>'
70155 cls : 'btn-group roo-upload-cropbox-trash',
70160 cls : 'btn btn-default',
70161 html : '<i class="fa fa-trash"></i>'
70167 cls : 'btn-group roo-upload-cropbox-rotate-right',
70168 action : 'rotate-right',
70172 cls : 'btn btn-default',
70173 html : '<i class="fa fa-repeat"></i>'
70181 cls : 'btn-group roo-upload-cropbox-rotate-left',
70182 action : 'rotate-left',
70186 cls : 'btn btn-default',
70187 html : '<i class="fa fa-undo"></i>'
70193 cls : 'btn-group roo-upload-cropbox-rotate-right',
70194 action : 'rotate-right',
70198 cls : 'btn btn-default',
70199 html : '<i class="fa fa-repeat"></i>'
70207 cls : 'btn-group roo-upload-cropbox-center',
70212 cls : 'btn btn-default',