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} css styling for resizing. (used on bootstrap only)
49968 * @cfg {Number} height (in pixels)
49972 * @cfg {Number} width (in pixels)
49976 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
49977 * if you are doing an email editor, this probably needs disabling, it's designed
49982 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
49984 enableBlocks : true,
49986 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
49989 stylesheets: false,
49991 * @cfg {String} language default en - language of text (usefull for rtl languages)
49997 * @cfg {boolean} allowComments - default false - allow comments in HTML source
49998 * - by default they are stripped - if you are editing email you may need this.
50000 allowComments: false,
50004 // private properties
50005 validationEvent : false,
50007 initialized : false,
50009 sourceEditMode : false,
50010 onFocus : Roo.emptyFn,
50012 hideMode:'offsets',
50016 // blacklist + whitelisted elements..
50023 undoManager : false,
50025 * Protected method that will not generally be called directly. It
50026 * is called when the editor initializes the iframe with HTML contents. Override this method if you
50027 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
50029 getDocMarkup : function(){
50033 // inherit styels from page...??
50034 if (this.stylesheets === false) {
50036 Roo.get(document.head).select('style').each(function(node) {
50037 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
50040 Roo.get(document.head).select('link').each(function(node) {
50041 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
50044 } else if (!this.stylesheets.length) {
50046 st = '<style type="text/css">' +
50047 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
50050 for (var i in this.stylesheets) {
50051 if (typeof(this.stylesheets[i]) != 'string') {
50054 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
50059 st += '<style type="text/css">' +
50060 'IMG { cursor: pointer } ' +
50063 st += '<meta name="google" content="notranslate">';
50065 var cls = 'notranslate roo-htmleditor-body';
50067 if(this.bodyCls.length){
50068 cls += ' ' + this.bodyCls;
50071 return '<html class="notranslate" translate="no"><head>' + st +
50072 //<style type="text/css">' +
50073 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
50075 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
50079 onRender : function(ct, position)
50082 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
50083 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
50086 this.el.dom.style.border = '0 none';
50087 this.el.dom.setAttribute('tabIndex', -1);
50088 this.el.addClass('x-hidden hide');
50092 if(Roo.isIE){ // fix IE 1px bogus margin
50093 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
50097 this.frameId = Roo.id();
50101 cls: 'form-control', // bootstrap..
50103 name: this.frameId,
50104 frameBorder : 'no',
50105 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
50108 ifcfg.style = { resize : this.resize };
50111 var iframe = this.owner.wrap.createChild(ifcfg, this.el);
50114 this.iframe = iframe.dom;
50116 this.assignDocWin();
50118 this.doc.designMode = 'on';
50121 this.doc.write(this.getDocMarkup());
50125 var task = { // must defer to wait for browser to be ready
50127 //console.log("run task?" + this.doc.readyState);
50128 this.assignDocWin();
50129 if(this.doc.body || this.doc.readyState == 'complete'){
50131 this.doc.designMode="on";
50136 Roo.TaskMgr.stop(task);
50137 this.initEditor.defer(10, this);
50144 Roo.TaskMgr.start(task);
50149 onResize : function(w, h)
50151 Roo.log('resize: ' +w + ',' + h );
50152 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
50156 if(typeof w == 'number'){
50158 this.iframe.style.width = w + 'px';
50160 if(typeof h == 'number'){
50162 this.iframe.style.height = h + 'px';
50164 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
50171 * Toggles the editor between standard and source edit mode.
50172 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
50174 toggleSourceEdit : function(sourceEditMode){
50176 this.sourceEditMode = sourceEditMode === true;
50178 if(this.sourceEditMode){
50180 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
50183 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
50184 //this.iframe.className = '';
50187 //this.setSize(this.owner.wrap.getSize());
50188 //this.fireEvent('editmodechange', this, this.sourceEditMode);
50195 * Protected method that will not generally be called directly. If you need/want
50196 * custom HTML cleanup, this is the method you should override.
50197 * @param {String} html The HTML to be cleaned
50198 * return {String} The cleaned HTML
50200 cleanHtml : function(html)
50202 html = String(html);
50203 if(html.length > 5){
50204 if(Roo.isSafari){ // strip safari nonsense
50205 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
50208 if(html == ' '){
50215 * HTML Editor -> Textarea
50216 * Protected method that will not generally be called directly. Syncs the contents
50217 * of the editor iframe with the textarea.
50219 syncValue : function()
50221 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
50222 if(this.initialized){
50224 if (this.undoManager) {
50225 this.undoManager.addEvent();
50229 var bd = (this.doc.body || this.doc.documentElement);
50232 var sel = this.win.getSelection();
50234 var div = document.createElement('div');
50235 div.innerHTML = bd.innerHTML;
50236 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
50237 if (gtx.length > 0) {
50238 var rm = gtx.item(0).parentNode;
50239 rm.parentNode.removeChild(rm);
50243 if (this.enableBlocks) {
50244 new Roo.htmleditor.FilterBlock({ node : div });
50247 var html = div.innerHTML;
50250 if (this.autoClean) {
50252 new Roo.htmleditor.FilterAttributes({
50273 attrib_clean : ['href', 'src' ]
50276 var tidy = new Roo.htmleditor.TidySerializer({
50279 html = tidy.serialize(div);
50285 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
50286 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
50288 html = '<div style="'+m[0]+'">' + html + '</div>';
50291 html = this.cleanHtml(html);
50292 // fix up the special chars.. normaly like back quotes in word...
50293 // however we do not want to do this with chinese..
50294 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
50296 var cc = match.charCodeAt();
50298 // Get the character value, handling surrogate pairs
50299 if (match.length == 2) {
50300 // It's a surrogate pair, calculate the Unicode code point
50301 var high = match.charCodeAt(0) - 0xD800;
50302 var low = match.charCodeAt(1) - 0xDC00;
50303 cc = (high * 0x400) + low + 0x10000;
50305 (cc >= 0x4E00 && cc < 0xA000 ) ||
50306 (cc >= 0x3400 && cc < 0x4E00 ) ||
50307 (cc >= 0xf900 && cc < 0xfb00 )
50312 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
50313 return "&#" + cc + ";";
50320 if(this.owner.fireEvent('beforesync', this, html) !== false){
50321 this.el.dom.value = html;
50322 this.owner.fireEvent('sync', this, html);
50328 * TEXTAREA -> EDITABLE
50329 * Protected method that will not generally be called directly. Pushes the value of the textarea
50330 * into the iframe editor.
50332 pushValue : function()
50334 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
50335 if(this.initialized){
50336 var v = this.el.dom.value.trim();
50339 if(this.owner.fireEvent('beforepush', this, v) !== false){
50340 var d = (this.doc.body || this.doc.documentElement);
50343 this.el.dom.value = d.innerHTML;
50344 this.owner.fireEvent('push', this, v);
50346 if (this.autoClean) {
50347 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
50348 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
50350 if (this.enableBlocks) {
50351 Roo.htmleditor.Block.initAll(this.doc.body);
50354 this.updateLanguage();
50356 var lc = this.doc.body.lastChild;
50357 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
50358 // add an extra line at the end.
50359 this.doc.body.appendChild(this.doc.createElement('br'));
50367 deferFocus : function(){
50368 this.focus.defer(10, this);
50372 focus : function(){
50373 if(this.win && !this.sourceEditMode){
50380 assignDocWin: function()
50382 var iframe = this.iframe;
50385 this.doc = iframe.contentWindow.document;
50386 this.win = iframe.contentWindow;
50388 // if (!Roo.get(this.frameId)) {
50391 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50392 // this.win = Roo.get(this.frameId).dom.contentWindow;
50394 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
50398 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50399 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
50404 initEditor : function(){
50405 //console.log("INIT EDITOR");
50406 this.assignDocWin();
50410 this.doc.designMode="on";
50412 this.doc.write(this.getDocMarkup());
50415 var dbody = (this.doc.body || this.doc.documentElement);
50416 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
50417 // this copies styles from the containing element into thsi one..
50418 // not sure why we need all of this..
50419 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
50421 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
50422 //ss['background-attachment'] = 'fixed'; // w3c
50423 dbody.bgProperties = 'fixed'; // ie
50424 dbody.setAttribute("translate", "no");
50426 //Roo.DomHelper.applyStyles(dbody, ss);
50427 Roo.EventManager.on(this.doc, {
50429 'mouseup': this.onEditorEvent,
50430 'dblclick': this.onEditorEvent,
50431 'click': this.onEditorEvent,
50432 'keyup': this.onEditorEvent,
50437 Roo.EventManager.on(this.doc, {
50438 'paste': this.onPasteEvent,
50442 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
50445 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
50446 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
50448 this.initialized = true;
50451 // initialize special key events - enter
50452 new Roo.htmleditor.KeyEnter({core : this});
50456 this.owner.fireEvent('initialize', this);
50459 // this is to prevent a href clicks resulting in a redirect?
50461 onPasteEvent : function(e,v)
50463 // I think we better assume paste is going to be a dirty load of rubish from word..
50465 // even pasting into a 'email version' of this widget will have to clean up that mess.
50466 var cd = (e.browserEvent.clipboardData || window.clipboardData);
50468 // check what type of paste - if it's an image, then handle it differently.
50469 if (cd.files && cd.files.length > 0) {
50471 var urlAPI = (window.createObjectURL && window) ||
50472 (window.URL && URL.revokeObjectURL && URL) ||
50473 (window.webkitURL && webkitURL);
50475 var url = urlAPI.createObjectURL( cd.files[0]);
50476 this.insertAtCursor('<img src=" + url + ">');
50479 if (cd.types.indexOf('text/html') < 0 ) {
50483 var html = cd.getData('text/html'); // clipboard event
50484 if (cd.types.indexOf('text/rtf') > -1) {
50485 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
50486 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
50491 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
50492 .map(function(g) { return g.toDataURL(); })
50493 .filter(function(g) { return g != 'about:blank'; });
50496 html = this.cleanWordChars(html);
50498 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
50501 var sn = this.getParentElement();
50502 // check if d contains a table, and prevent nesting??
50503 //Roo.log(d.getElementsByTagName('table'));
50505 //Roo.log(sn.closest('table'));
50506 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
50507 e.preventDefault();
50508 this.insertAtCursor("You can not nest tables");
50509 //Roo.log("prevent?"); // fixme -
50515 if (images.length > 0) {
50516 // replace all v:imagedata - with img.
50517 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
50518 Roo.each(ar, function(node) {
50519 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
50520 node.parentNode.removeChild(node);
50524 Roo.each(d.getElementsByTagName('img'), function(img, i) {
50525 img.setAttribute('src', images[i]);
50528 if (this.autoClean) {
50529 new Roo.htmleditor.FilterWord({ node : d });
50531 new Roo.htmleditor.FilterStyleToTag({ node : d });
50532 new Roo.htmleditor.FilterAttributes({
50534 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
50535 attrib_clean : ['href', 'src' ]
50537 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
50538 // should be fonts..
50539 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
50540 new Roo.htmleditor.FilterParagraph({ node : d });
50541 new Roo.htmleditor.FilterSpan({ node : d });
50542 new Roo.htmleditor.FilterLongBr({ node : d });
50543 new Roo.htmleditor.FilterComment({ node : d });
50547 if (this.enableBlocks) {
50549 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
50550 if (img.closest('figure')) { // assume!! that it's aready
50553 var fig = new Roo.htmleditor.BlockFigure({
50554 image_src : img.src
50556 fig.updateElement(img); // replace it..
50562 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
50563 if (this.enableBlocks) {
50564 Roo.htmleditor.Block.initAll(this.doc.body);
50568 e.preventDefault();
50569 this.owner.fireEvent('paste', this);
50571 // default behaveiour should be our local cleanup paste? (optional?)
50572 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
50573 //this.owner.fireEvent('paste', e, v);
50576 onDestroy : function(){
50582 //for (var i =0; i < this.toolbars.length;i++) {
50583 // // fixme - ask toolbars for heights?
50584 // this.toolbars[i].onDestroy();
50587 //this.wrap.dom.innerHTML = '';
50588 //this.wrap.remove();
50593 onFirstFocus : function(){
50595 this.assignDocWin();
50596 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
50598 this.activated = true;
50601 if(Roo.isGecko){ // prevent silly gecko errors
50603 var s = this.win.getSelection();
50604 if(!s.focusNode || s.focusNode.nodeType != 3){
50605 var r = s.getRangeAt(0);
50606 r.selectNodeContents((this.doc.body || this.doc.documentElement));
50611 this.execCmd('useCSS', true);
50612 this.execCmd('styleWithCSS', false);
50615 this.owner.fireEvent('activate', this);
50619 adjustFont: function(btn){
50620 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
50621 //if(Roo.isSafari){ // safari
50624 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
50625 if(Roo.isSafari){ // safari
50626 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
50627 v = (v < 10) ? 10 : v;
50628 v = (v > 48) ? 48 : v;
50629 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
50634 v = Math.max(1, v+adjust);
50636 this.execCmd('FontSize', v );
50639 onEditorEvent : function(e)
50643 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
50644 return; // we do not handle this.. (undo manager does..)
50646 // in theory this detects if the last element is not a br, then we try and do that.
50647 // its so clicking in space at bottom triggers adding a br and moving the cursor.
50649 e.target.nodeName == 'BODY' &&
50650 e.type == "mouseup" &&
50651 this.doc.body.lastChild
50653 var lc = this.doc.body.lastChild;
50654 // gtx-trans is google translate plugin adding crap.
50655 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
50656 lc = lc.previousSibling;
50658 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
50659 // if last element is <BR> - then dont do anything.
50661 var ns = this.doc.createElement('br');
50662 this.doc.body.appendChild(ns);
50663 range = this.doc.createRange();
50664 range.setStartAfter(ns);
50665 range.collapse(true);
50666 var sel = this.win.getSelection();
50667 sel.removeAllRanges();
50668 sel.addRange(range);
50674 this.fireEditorEvent(e);
50675 // this.updateToolbar();
50676 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
50679 fireEditorEvent: function(e)
50681 this.owner.fireEvent('editorevent', this, e);
50684 insertTag : function(tg)
50686 // could be a bit smarter... -> wrap the current selected tRoo..
50687 if (tg.toLowerCase() == 'span' ||
50688 tg.toLowerCase() == 'code' ||
50689 tg.toLowerCase() == 'sup' ||
50690 tg.toLowerCase() == 'sub'
50693 range = this.createRange(this.getSelection());
50694 var wrappingNode = this.doc.createElement(tg.toLowerCase());
50695 wrappingNode.appendChild(range.extractContents());
50696 range.insertNode(wrappingNode);
50703 this.execCmd("formatblock", tg);
50704 this.undoManager.addEvent();
50707 insertText : function(txt)
50711 var range = this.createRange();
50712 range.deleteContents();
50713 //alert(Sender.getAttribute('label'));
50715 range.insertNode(this.doc.createTextNode(txt));
50716 this.undoManager.addEvent();
50722 * Executes a Midas editor command on the editor document and performs necessary focus and
50723 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
50724 * @param {String} cmd The Midas command
50725 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50727 relayCmd : function(cmd, value)
50731 case 'justifyleft':
50732 case 'justifyright':
50733 case 'justifycenter':
50734 // if we are in a cell, then we will adjust the
50735 var n = this.getParentElement();
50736 var td = n.closest('td');
50738 var bl = Roo.htmleditor.Block.factory(td);
50739 bl.textAlign = cmd.replace('justify','');
50740 bl.updateElement();
50741 this.owner.fireEvent('editorevent', this);
50744 this.execCmd('styleWithCSS', true); //
50748 // if there is no selection, then we insert, and set the curson inside it..
50749 this.execCmd('styleWithCSS', false);
50759 this.execCmd(cmd, value);
50760 this.owner.fireEvent('editorevent', this);
50761 //this.updateToolbar();
50762 this.owner.deferFocus();
50766 * Executes a Midas editor command directly on the editor document.
50767 * For visual commands, you should use {@link #relayCmd} instead.
50768 * <b>This should only be called after the editor is initialized.</b>
50769 * @param {String} cmd The Midas command
50770 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50772 execCmd : function(cmd, value){
50773 this.doc.execCommand(cmd, false, value === undefined ? null : value);
50780 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
50782 * @param {String} text | dom node..
50784 insertAtCursor : function(text)
50787 if(!this.activated){
50791 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
50795 // from jquery ui (MIT licenced)
50797 var win = this.win;
50799 if (win.getSelection && win.getSelection().getRangeAt) {
50801 // delete the existing?
50803 this.createRange(this.getSelection()).deleteContents();
50804 range = win.getSelection().getRangeAt(0);
50805 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
50806 range.insertNode(node);
50807 range = range.cloneRange();
50808 range.collapse(false);
50810 win.getSelection().removeAllRanges();
50811 win.getSelection().addRange(range);
50815 } else if (win.document.selection && win.document.selection.createRange) {
50816 // no firefox support
50817 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50818 win.document.selection.createRange().pasteHTML(txt);
50821 // no firefox support
50822 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50823 this.execCmd('InsertHTML', txt);
50831 mozKeyPress : function(e){
50833 var c = e.getCharCode(), cmd;
50836 c = String.fromCharCode(c).toLowerCase();
50850 // this.cleanUpPaste.defer(100, this);
50856 this.relayCmd(cmd);
50857 //this.win.focus();
50858 //this.execCmd(cmd);
50859 //this.deferFocus();
50860 e.preventDefault();
50868 fixKeys : function(){ // load time branching for fastest keydown performance
50872 return function(e){
50873 var k = e.getKey(), r;
50876 r = this.doc.selection.createRange();
50879 r.pasteHTML('    ');
50884 /// this is handled by Roo.htmleditor.KeyEnter
50887 r = this.doc.selection.createRange();
50889 var target = r.parentElement();
50890 if(!target || target.tagName.toLowerCase() != 'li'){
50892 r.pasteHTML('<br/>');
50899 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50900 // this.cleanUpPaste.defer(100, this);
50906 }else if(Roo.isOpera){
50907 return function(e){
50908 var k = e.getKey();
50912 this.execCmd('InsertHTML','    ');
50916 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50917 // this.cleanUpPaste.defer(100, this);
50922 }else if(Roo.isSafari){
50923 return function(e){
50924 var k = e.getKey();
50928 this.execCmd('InsertText','\t');
50932 this.mozKeyPress(e);
50934 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50935 // this.cleanUpPaste.defer(100, this);
50943 getAllAncestors: function()
50945 var p = this.getSelectedNode();
50948 a.push(p); // push blank onto stack..
50949 p = this.getParentElement();
50953 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
50957 a.push(this.doc.body);
50961 lastSelNode : false,
50964 getSelection : function()
50966 this.assignDocWin();
50967 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
50970 * Select a dom node
50971 * @param {DomElement} node the node to select
50973 selectNode : function(node, collapse)
50975 var nodeRange = node.ownerDocument.createRange();
50977 nodeRange.selectNode(node);
50979 nodeRange.selectNodeContents(node);
50981 if (collapse === true) {
50982 nodeRange.collapse(true);
50985 var s = this.win.getSelection();
50986 s.removeAllRanges();
50987 s.addRange(nodeRange);
50990 getSelectedNode: function()
50992 // this may only work on Gecko!!!
50994 // should we cache this!!!!
50998 var range = this.createRange(this.getSelection()).cloneRange();
51001 var parent = range.parentElement();
51003 var testRange = range.duplicate();
51004 testRange.moveToElementText(parent);
51005 if (testRange.inRange(range)) {
51008 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
51011 parent = parent.parentElement;
51016 // is ancestor a text element.
51017 var ac = range.commonAncestorContainer;
51018 if (ac.nodeType == 3) {
51019 ac = ac.parentNode;
51022 var ar = ac.childNodes;
51025 var other_nodes = [];
51026 var has_other_nodes = false;
51027 for (var i=0;i<ar.length;i++) {
51028 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
51031 // fullly contained node.
51033 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
51038 // probably selected..
51039 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
51040 other_nodes.push(ar[i]);
51044 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
51049 has_other_nodes = true;
51051 if (!nodes.length && other_nodes.length) {
51052 nodes= other_nodes;
51054 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
51062 createRange: function(sel)
51064 // this has strange effects when using with
51065 // top toolbar - not sure if it's a great idea.
51066 //this.editor.contentWindow.focus();
51067 if (typeof sel != "undefined") {
51069 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
51071 return this.doc.createRange();
51074 return this.doc.createRange();
51077 getParentElement: function()
51080 this.assignDocWin();
51081 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
51083 var range = this.createRange(sel);
51086 var p = range.commonAncestorContainer;
51087 while (p.nodeType == 3) { // text node
51098 * Range intersection.. the hard stuff...
51102 * [ -- selected range --- ]
51106 * if end is before start or hits it. fail.
51107 * if start is after end or hits it fail.
51109 * if either hits (but other is outside. - then it's not
51115 // @see http://www.thismuchiknow.co.uk/?p=64.
51116 rangeIntersectsNode : function(range, node)
51118 var nodeRange = node.ownerDocument.createRange();
51120 nodeRange.selectNode(node);
51122 nodeRange.selectNodeContents(node);
51125 var rangeStartRange = range.cloneRange();
51126 rangeStartRange.collapse(true);
51128 var rangeEndRange = range.cloneRange();
51129 rangeEndRange.collapse(false);
51131 var nodeStartRange = nodeRange.cloneRange();
51132 nodeStartRange.collapse(true);
51134 var nodeEndRange = nodeRange.cloneRange();
51135 nodeEndRange.collapse(false);
51137 return rangeStartRange.compareBoundaryPoints(
51138 Range.START_TO_START, nodeEndRange) == -1 &&
51139 rangeEndRange.compareBoundaryPoints(
51140 Range.START_TO_START, nodeStartRange) == 1;
51144 rangeCompareNode : function(range, node)
51146 var nodeRange = node.ownerDocument.createRange();
51148 nodeRange.selectNode(node);
51150 nodeRange.selectNodeContents(node);
51154 range.collapse(true);
51156 nodeRange.collapse(true);
51158 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
51159 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
51161 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
51163 var nodeIsBefore = ss == 1;
51164 var nodeIsAfter = ee == -1;
51166 if (nodeIsBefore && nodeIsAfter) {
51169 if (!nodeIsBefore && nodeIsAfter) {
51170 return 1; //right trailed.
51173 if (nodeIsBefore && !nodeIsAfter) {
51174 return 2; // left trailed.
51180 cleanWordChars : function(input) {// change the chars to hex code
51183 [ 8211, "–" ],
51184 [ 8212, "—" ],
51192 var output = input;
51193 Roo.each(swapCodes, function(sw) {
51194 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
51196 output = output.replace(swapper, sw[1]);
51206 cleanUpChild : function (node)
51209 new Roo.htmleditor.FilterComment({node : node});
51210 new Roo.htmleditor.FilterAttributes({
51212 attrib_black : this.ablack,
51213 attrib_clean : this.aclean,
51214 style_white : this.cwhite,
51215 style_black : this.cblack
51217 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
51218 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
51224 * Clean up MS wordisms...
51225 * @deprecated - use filter directly
51227 cleanWord : function(node)
51229 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
51230 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
51237 * @deprecated - use filters
51239 cleanTableWidths : function(node)
51241 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
51248 applyBlacklists : function()
51250 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
51251 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
51253 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
51254 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
51255 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
51259 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
51260 if (b.indexOf(tag) > -1) {
51263 this.white.push(tag);
51267 Roo.each(w, function(tag) {
51268 if (b.indexOf(tag) > -1) {
51271 if (this.white.indexOf(tag) > -1) {
51274 this.white.push(tag);
51279 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
51280 if (w.indexOf(tag) > -1) {
51283 this.black.push(tag);
51287 Roo.each(b, function(tag) {
51288 if (w.indexOf(tag) > -1) {
51291 if (this.black.indexOf(tag) > -1) {
51294 this.black.push(tag);
51299 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
51300 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
51304 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
51305 if (b.indexOf(tag) > -1) {
51308 this.cwhite.push(tag);
51312 Roo.each(w, function(tag) {
51313 if (b.indexOf(tag) > -1) {
51316 if (this.cwhite.indexOf(tag) > -1) {
51319 this.cwhite.push(tag);
51324 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
51325 if (w.indexOf(tag) > -1) {
51328 this.cblack.push(tag);
51332 Roo.each(b, function(tag) {
51333 if (w.indexOf(tag) > -1) {
51336 if (this.cblack.indexOf(tag) > -1) {
51339 this.cblack.push(tag);
51344 setStylesheets : function(stylesheets)
51346 if(typeof(stylesheets) == 'string'){
51347 Roo.get(this.iframe.contentDocument.head).createChild({
51349 rel : 'stylesheet',
51358 Roo.each(stylesheets, function(s) {
51363 Roo.get(_this.iframe.contentDocument.head).createChild({
51365 rel : 'stylesheet',
51375 updateLanguage : function()
51377 if (!this.iframe || !this.iframe.contentDocument) {
51380 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
51384 removeStylesheets : function()
51388 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
51393 setStyle : function(style)
51395 Roo.get(this.iframe.contentDocument.head).createChild({
51404 // hide stuff that is not compatible
51418 * @event specialkey
51422 * @cfg {String} fieldClass @hide
51425 * @cfg {String} focusClass @hide
51428 * @cfg {String} autoCreate @hide
51431 * @cfg {String} inputType @hide
51434 * @cfg {String} invalidClass @hide
51437 * @cfg {String} invalidText @hide
51440 * @cfg {String} msgFx @hide
51443 * @cfg {String} validateOnBlur @hide
51447 Roo.HtmlEditorCore.white = [
51448 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
51450 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
51451 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
51452 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
51453 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
51454 'TABLE', 'UL', 'XMP',
51456 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
51459 'DIR', 'MENU', 'OL', 'UL', 'DL',
51465 Roo.HtmlEditorCore.black = [
51466 // 'embed', 'object', // enable - backend responsiblity to clean thiese
51468 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
51469 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
51470 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
51471 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
51472 //'FONT' // CLEAN LATER..
51473 'COLGROUP', 'COL' // messy tables.
51477 Roo.HtmlEditorCore.clean = [ // ?? needed???
51478 'SCRIPT', 'STYLE', 'TITLE', 'XML'
51480 Roo.HtmlEditorCore.tag_remove = [
51485 Roo.HtmlEditorCore.ablack = [
51489 Roo.HtmlEditorCore.aclean = [
51490 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
51494 Roo.HtmlEditorCore.pwhite= [
51495 'http', 'https', 'mailto'
51498 // white listed style attributes.
51499 Roo.HtmlEditorCore.cwhite= [
51500 // 'text-align', /// default is to allow most things..
51506 // black listed style attributes.
51507 Roo.HtmlEditorCore.cblack= [
51508 // 'font-size' -- this can be set by the project
51514 //<script type="text/javascript">
51517 * Ext JS Library 1.1.1
51518 * Copyright(c) 2006-2007, Ext JS, LLC.
51524 Roo.form.HtmlEditor = function(config){
51528 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
51530 if (!this.toolbars) {
51531 this.toolbars = [];
51533 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
51539 * @class Roo.form.HtmlEditor
51540 * @extends Roo.form.Field
51541 * Provides a lightweight HTML Editor component.
51543 * This has been tested on Fireforx / Chrome.. IE may not be so great..
51545 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
51546 * supported by this editor.</b><br/><br/>
51547 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
51548 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
51550 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
51552 * @cfg {Boolean} clearUp
51556 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
51561 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
51566 * @cfg {Number} height (in pixels)
51570 * @cfg {Number} width (in pixels)
51575 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea rootURL + '/roojs1/css/undoreset.css', .
51578 stylesheets: false,
51582 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
51587 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
51593 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
51598 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
51603 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
51605 allowComments: false,
51607 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
51609 enableBlocks : true,
51612 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
51613 * if you are doing an email editor, this probably needs disabling, it's designed
51617 * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
51621 * @cfg {String} language default en - language of text (usefull for rtl languages)
51630 // private properties
51631 validationEvent : false,
51633 initialized : false,
51636 onFocus : Roo.emptyFn,
51638 hideMode:'offsets',
51640 actionMode : 'container', // defaults to hiding it...
51642 defaultAutoCreate : { // modified by initCompnoent..
51644 style:"width:500px;height:300px;",
51645 autocomplete: "new-password"
51649 initComponent : function(){
51652 * @event initialize
51653 * Fires when the editor is fully initialized (including the iframe)
51654 * @param {HtmlEditor} this
51659 * Fires when the editor is first receives the focus. Any insertion must wait
51660 * until after this event.
51661 * @param {HtmlEditor} this
51665 * @event beforesync
51666 * Fires before the textarea is updated with content from the editor iframe. Return false
51667 * to cancel the sync.
51668 * @param {HtmlEditor} this
51669 * @param {String} html
51673 * @event beforepush
51674 * Fires before the iframe editor is updated with content from the textarea. Return false
51675 * to cancel the push.
51676 * @param {HtmlEditor} this
51677 * @param {String} html
51682 * Fires when the textarea is updated with content from the editor iframe.
51683 * @param {HtmlEditor} this
51684 * @param {String} html
51689 * Fires when the iframe editor is updated with content from the textarea.
51690 * @param {HtmlEditor} this
51691 * @param {String} html
51695 * @event editmodechange
51696 * Fires when the editor switches edit modes
51697 * @param {HtmlEditor} this
51698 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
51700 editmodechange: true,
51702 * @event editorevent
51703 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
51704 * @param {HtmlEditor} this
51708 * @event firstfocus
51709 * Fires when on first focus - needed by toolbars..
51710 * @param {HtmlEditor} this
51715 * Auto save the htmlEditor value as a file into Events
51716 * @param {HtmlEditor} this
51720 * @event savedpreview
51721 * preview the saved version of htmlEditor
51722 * @param {HtmlEditor} this
51724 savedpreview: true,
51727 * @event stylesheetsclick
51728 * Fires when press the Sytlesheets button
51729 * @param {Roo.HtmlEditorCore} this
51731 stylesheetsclick: true,
51734 * Fires when press user pastes into the editor
51735 * @param {Roo.HtmlEditorCore} this
51739 this.defaultAutoCreate = {
51741 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
51742 autocomplete: "new-password"
51747 * Protected method that will not generally be called directly. It
51748 * is called when the editor creates its toolbar. Override this method if you need to
51749 * add custom toolbar buttons.
51750 * @param {HtmlEditor} editor
51752 createToolbar : function(editor){
51753 Roo.log("create toolbars");
51754 if (!editor.toolbars || !editor.toolbars.length) {
51755 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
51758 for (var i =0 ; i < editor.toolbars.length;i++) {
51759 editor.toolbars[i] = Roo.factory(
51760 typeof(editor.toolbars[i]) == 'string' ?
51761 { xtype: editor.toolbars[i]} : editor.toolbars[i],
51762 Roo.form.HtmlEditor);
51763 editor.toolbars[i].init(editor);
51769 * get the Context selected node
51770 * @returns {DomElement|boolean} selected node if active or false if none
51773 getSelectedNode : function()
51775 if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
51778 return this.toolbars[1].tb.selectedNode;
51782 onRender : function(ct, position)
51785 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
51787 this.wrap = this.el.wrap({
51788 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
51791 this.editorcore.onRender(ct, position);
51793 if (this.resizable) {
51794 this.resizeEl = new Roo.Resizable(this.wrap, {
51798 minHeight : this.height,
51799 height: this.height,
51800 handles : this.resizable,
51803 resize : function(r, w, h) {
51804 _t.onResize(w,h); // -something
51810 this.createToolbar(this);
51814 this.setSize(this.wrap.getSize());
51816 if (this.resizeEl) {
51817 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
51818 // should trigger onReize..
51821 this.keyNav = new Roo.KeyNav(this.el, {
51823 "tab" : function(e){
51824 e.preventDefault();
51826 var value = this.getValue();
51828 var start = this.el.dom.selectionStart;
51829 var end = this.el.dom.selectionEnd;
51833 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
51834 this.el.dom.setSelectionRange(end + 1, end + 1);
51838 var f = value.substring(0, start).split("\t");
51840 if(f.pop().length != 0){
51844 this.setValue(f.join("\t") + value.substring(end));
51845 this.el.dom.setSelectionRange(start - 1, start - 1);
51849 "home" : function(e){
51850 e.preventDefault();
51852 var curr = this.el.dom.selectionStart;
51853 var lines = this.getValue().split("\n");
51860 this.el.dom.setSelectionRange(0, 0);
51866 for (var i = 0; i < lines.length;i++) {
51867 pos += lines[i].length;
51877 pos -= lines[i].length;
51883 this.el.dom.setSelectionRange(pos, pos);
51887 this.el.dom.selectionStart = pos;
51888 this.el.dom.selectionEnd = curr;
51891 "end" : function(e){
51892 e.preventDefault();
51894 var curr = this.el.dom.selectionStart;
51895 var lines = this.getValue().split("\n");
51902 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
51908 for (var i = 0; i < lines.length;i++) {
51910 pos += lines[i].length;
51924 this.el.dom.setSelectionRange(pos, pos);
51928 this.el.dom.selectionStart = curr;
51929 this.el.dom.selectionEnd = pos;
51934 doRelay : function(foo, bar, hname){
51935 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
51941 // if(this.autosave && this.w){
51942 // this.autoSaveFn = setInterval(this.autosave, 1000);
51947 onResize : function(w, h)
51949 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
51954 if(typeof w == 'number'){
51955 var aw = w - this.wrap.getFrameWidth('lr');
51956 this.el.setWidth(this.adjustWidth('textarea', aw));
51959 if(typeof h == 'number'){
51961 for (var i =0; i < this.toolbars.length;i++) {
51962 // fixme - ask toolbars for heights?
51963 tbh += this.toolbars[i].tb.el.getHeight();
51964 if (this.toolbars[i].footer) {
51965 tbh += this.toolbars[i].footer.el.getHeight();
51972 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
51973 ah -= 5; // knock a few pixes off for look..
51975 this.el.setHeight(this.adjustWidth('textarea', ah));
51979 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
51980 this.editorcore.onResize(ew,eh);
51985 * Toggles the editor between standard and source edit mode.
51986 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
51988 toggleSourceEdit : function(sourceEditMode)
51990 this.editorcore.toggleSourceEdit(sourceEditMode);
51992 if(this.editorcore.sourceEditMode){
51993 Roo.log('editor - showing textarea');
51996 // Roo.log(this.syncValue());
51997 this.editorcore.syncValue();
51998 this.el.removeClass('x-hidden');
51999 this.el.dom.removeAttribute('tabIndex');
52001 this.el.dom.scrollTop = 0;
52004 for (var i = 0; i < this.toolbars.length; i++) {
52005 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
52006 this.toolbars[i].tb.hide();
52007 this.toolbars[i].footer.hide();
52012 Roo.log('editor - hiding textarea');
52014 // Roo.log(this.pushValue());
52015 this.editorcore.pushValue();
52017 this.el.addClass('x-hidden');
52018 this.el.dom.setAttribute('tabIndex', -1);
52020 for (var i = 0; i < this.toolbars.length; i++) {
52021 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
52022 this.toolbars[i].tb.show();
52023 this.toolbars[i].footer.show();
52027 //this.deferFocus();
52030 this.setSize(this.wrap.getSize());
52031 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
52033 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
52036 // private (for BoxComponent)
52037 adjustSize : Roo.BoxComponent.prototype.adjustSize,
52039 // private (for BoxComponent)
52040 getResizeEl : function(){
52044 // private (for BoxComponent)
52045 getPositionEl : function(){
52050 initEvents : function(){
52051 this.originalValue = this.getValue();
52055 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
52058 markInvalid : Roo.emptyFn,
52060 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
52063 clearInvalid : Roo.emptyFn,
52065 setValue : function(v){
52066 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
52067 this.editorcore.pushValue();
52071 * update the language in the body - really done by core
52072 * @param {String} language - eg. en / ar / zh-CN etc..
52074 updateLanguage : function(lang)
52076 this.language = lang;
52077 this.editorcore.language = lang;
52078 this.editorcore.updateLanguage();
52082 deferFocus : function(){
52083 this.focus.defer(10, this);
52087 focus : function(){
52088 this.editorcore.focus();
52094 onDestroy : function(){
52100 for (var i =0; i < this.toolbars.length;i++) {
52101 // fixme - ask toolbars for heights?
52102 this.toolbars[i].onDestroy();
52105 this.wrap.dom.innerHTML = '';
52106 this.wrap.remove();
52111 onFirstFocus : function(){
52112 //Roo.log("onFirstFocus");
52113 this.editorcore.onFirstFocus();
52114 for (var i =0; i < this.toolbars.length;i++) {
52115 this.toolbars[i].onFirstFocus();
52121 syncValue : function()
52123 this.editorcore.syncValue();
52126 pushValue : function()
52128 this.editorcore.pushValue();
52131 setStylesheets : function(stylesheets)
52133 this.editorcore.setStylesheets(stylesheets);
52136 removeStylesheets : function()
52138 this.editorcore.removeStylesheets();
52142 // hide stuff that is not compatible
52156 * @event specialkey
52160 * @cfg {String} fieldClass @hide
52163 * @cfg {String} focusClass @hide
52166 * @cfg {String} autoCreate @hide
52169 * @cfg {String} inputType @hide
52172 * @cfg {String} invalidClass @hide
52175 * @cfg {String} invalidText @hide
52178 * @cfg {String} msgFx @hide
52181 * @cfg {String} validateOnBlur @hide
52187 * Ext JS Library 1.1.1
52188 * Copyright(c) 2006-2007, Ext JS, LLC.
52194 * @class Roo.form.HtmlEditor.ToolbarStandard
52199 new Roo.form.HtmlEditor({
52202 new Roo.form.HtmlEditorToolbar1({
52203 disable : { fonts: 1 , format: 1, ..., ... , ...],
52209 * @cfg {Object} disable List of elements to disable..
52210 * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
52214 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
52217 Roo.form.HtmlEditor.ToolbarStandard = function(config)
52220 Roo.apply(this, config);
52222 // default disabled, based on 'good practice'..
52223 this.disable = this.disable || {};
52224 Roo.applyIf(this.disable, {
52227 specialElements : true
52231 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
52232 // dont call parent... till later.
52235 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
52242 editorcore : false,
52244 * @cfg {Object} disable List of toolbar elements to disable
52251 * @cfg {String} createLinkText The default text for the create link prompt
52253 createLinkText : 'Please enter the URL for the link:',
52255 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
52257 defaultLinkValue : 'http:/'+'/',
52261 * @cfg {Array} fontFamilies An array of available font families
52279 // "á" , ?? a acute?
52284 "°" // , // degrees
52286 // "é" , // e ecute
52287 // "ú" , // u ecute?
52290 specialElements : [
52292 text: "Insert Table",
52295 ihtml : '<table><tr><td>Cell</td></tr></table>'
52299 text: "Insert Image",
52302 ihtml : '<img src="about:blank"/>'
52311 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
52312 "input:submit", "input:button", "select", "textarea", "label" ],
52315 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
52317 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
52326 * @cfg {String} defaultFont default font to use.
52328 defaultFont: 'tahoma',
52330 fontSelect : false,
52333 formatCombo : false,
52335 init : function(editor)
52337 this.editor = editor;
52338 this.editorcore = editor.editorcore ? editor.editorcore : editor;
52339 var editorcore = this.editorcore;
52343 var fid = editorcore.frameId;
52345 function btn(id, toggle, handler){
52346 var xid = fid + '-'+ id ;
52350 cls : 'x-btn-icon x-edit-'+id,
52351 enableToggle:toggle !== false,
52352 scope: _t, // was editor...
52353 handler:handler||_t.relayBtnCmd,
52354 clickEvent:'mousedown',
52355 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52362 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52364 // stop form submits
52365 tb.el.on('click', function(e){
52366 e.preventDefault(); // what does this do?
52369 if(!this.disable.font) { // && !Roo.isSafari){
52370 /* why no safari for fonts
52371 editor.fontSelect = tb.el.createChild({
52374 cls:'x-font-select',
52375 html: this.createFontOptions()
52378 editor.fontSelect.on('change', function(){
52379 var font = editor.fontSelect.dom.value;
52380 editor.relayCmd('fontname', font);
52381 editor.deferFocus();
52385 editor.fontSelect.dom,
52391 if(!this.disable.formats){
52392 this.formatCombo = new Roo.form.ComboBox({
52393 store: new Roo.data.SimpleStore({
52396 data : this.formats // from states.js
52400 //autoCreate : {tag: "div", size: "20"},
52401 displayField:'tag',
52405 triggerAction: 'all',
52406 emptyText:'Add tag',
52407 selectOnFocus:true,
52410 'select': function(c, r, i) {
52411 editorcore.insertTag(r.get('tag'));
52417 tb.addField(this.formatCombo);
52421 if(!this.disable.format){
52426 btn('strikethrough')
52429 if(!this.disable.fontSize){
52434 btn('increasefontsize', false, editorcore.adjustFont),
52435 btn('decreasefontsize', false, editorcore.adjustFont)
52440 if(!this.disable.colors){
52443 id:editorcore.frameId +'-forecolor',
52444 cls:'x-btn-icon x-edit-forecolor',
52445 clickEvent:'mousedown',
52446 tooltip: this.buttonTips['forecolor'] || undefined,
52448 menu : new Roo.menu.ColorMenu({
52449 allowReselect: true,
52450 focus: Roo.emptyFn,
52453 selectHandler: function(cp, color){
52454 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
52455 editor.deferFocus();
52458 clickEvent:'mousedown'
52461 id:editorcore.frameId +'backcolor',
52462 cls:'x-btn-icon x-edit-backcolor',
52463 clickEvent:'mousedown',
52464 tooltip: this.buttonTips['backcolor'] || undefined,
52466 menu : new Roo.menu.ColorMenu({
52467 focus: Roo.emptyFn,
52470 allowReselect: true,
52471 selectHandler: function(cp, color){
52473 editorcore.execCmd('useCSS', false);
52474 editorcore.execCmd('hilitecolor', color);
52475 editorcore.execCmd('useCSS', true);
52476 editor.deferFocus();
52478 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
52479 Roo.isSafari || Roo.isIE ? '#'+color : color);
52480 editor.deferFocus();
52484 clickEvent:'mousedown'
52489 // now add all the items...
52492 if(!this.disable.alignments){
52495 btn('justifyleft'),
52496 btn('justifycenter'),
52497 btn('justifyright')
52501 //if(!Roo.isSafari){
52502 if(!this.disable.links){
52505 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
52509 if(!this.disable.lists){
52512 btn('insertorderedlist'),
52513 btn('insertunorderedlist')
52516 if(!this.disable.sourceEdit){
52519 btn('sourceedit', true, function(btn){
52520 this.toggleSourceEdit(btn.pressed);
52527 // special menu.. - needs to be tidied up..
52528 if (!this.disable.special) {
52531 cls: 'x-edit-none',
52537 for (var i =0; i < this.specialChars.length; i++) {
52538 smenu.menu.items.push({
52540 html: this.specialChars[i],
52541 handler: function(a,b) {
52542 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
52543 //editor.insertAtCursor(a.html);
52557 if (!this.disable.cleanStyles) {
52559 cls: 'x-btn-icon x-btn-clear',
52565 for (var i =0; i < this.cleanStyles.length; i++) {
52566 cmenu.menu.items.push({
52567 actiontype : this.cleanStyles[i],
52568 html: 'Remove ' + this.cleanStyles[i],
52569 handler: function(a,b) {
52572 var c = Roo.get(editorcore.doc.body);
52573 c.select('[style]').each(function(s) {
52574 s.dom.style.removeProperty(a.actiontype);
52576 editorcore.syncValue();
52581 cmenu.menu.items.push({
52582 actiontype : 'tablewidths',
52583 html: 'Remove Table Widths',
52584 handler: function(a,b) {
52585 editorcore.cleanTableWidths();
52586 editorcore.syncValue();
52590 cmenu.menu.items.push({
52591 actiontype : 'word',
52592 html: 'Remove MS Word Formating',
52593 handler: function(a,b) {
52594 editorcore.cleanWord();
52595 editorcore.syncValue();
52600 cmenu.menu.items.push({
52601 actiontype : 'all',
52602 html: 'Remove All Styles',
52603 handler: function(a,b) {
52605 var c = Roo.get(editorcore.doc.body);
52606 c.select('[style]').each(function(s) {
52607 s.dom.removeAttribute('style');
52609 editorcore.syncValue();
52614 cmenu.menu.items.push({
52615 actiontype : 'all',
52616 html: 'Remove All CSS Classes',
52617 handler: function(a,b) {
52619 var c = Roo.get(editorcore.doc.body);
52620 c.select('[class]').each(function(s) {
52621 s.dom.removeAttribute('class');
52623 editorcore.cleanWord();
52624 editorcore.syncValue();
52629 cmenu.menu.items.push({
52630 actiontype : 'tidy',
52631 html: 'Tidy HTML Source',
52632 handler: function(a,b) {
52633 new Roo.htmleditor.Tidy(editorcore.doc.body);
52634 editorcore.syncValue();
52643 if (!this.disable.specialElements) {
52646 cls: 'x-edit-none',
52651 for (var i =0; i < this.specialElements.length; i++) {
52652 semenu.menu.items.push(
52654 handler: function(a,b) {
52655 editor.insertAtCursor(this.ihtml);
52657 }, this.specialElements[i])
52669 for(var i =0; i< this.btns.length;i++) {
52670 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
52671 b.cls = 'x-edit-none';
52673 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
52674 b.cls += ' x-init-enable';
52677 b.scope = editorcore;
52685 // disable everything...
52687 this.tb.items.each(function(item){
52690 item.id != editorcore.frameId+ '-sourceedit' &&
52691 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
52697 this.rendered = true;
52699 // the all the btns;
52700 editor.on('editorevent', this.updateToolbar, this);
52701 // other toolbars need to implement this..
52702 //editor.on('editmodechange', this.updateToolbar, this);
52706 relayBtnCmd : function(btn) {
52707 this.editorcore.relayCmd(btn.cmd);
52709 // private used internally
52710 createLink : function(){
52711 //Roo.log("create link?");
52712 var ec = this.editorcore;
52713 var ar = ec.getAllAncestors();
52715 for(var i = 0;i< ar.length;i++) {
52716 if (ar[i] && ar[i].nodeName == 'A') {
52724 Roo.MessageBox.show({
52725 title : "Add / Edit Link URL",
52726 msg : "Enter the url for the link",
52727 buttons: Roo.MessageBox.OKCANCEL,
52728 fn: function(btn, url){
52732 if(url && url != 'http:/'+'/'){
52734 n.setAttribute('href', url);
52736 ec.relayCmd('createlink', url);
52742 //multiline: multiline,
52744 value : n ? n.getAttribute('href') : ''
52748 }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
52754 * Protected method that will not generally be called directly. It triggers
52755 * a toolbar update by reading the markup state of the current selection in the editor.
52757 updateToolbar: function(){
52759 if(!this.editorcore.activated){
52760 this.editor.onFirstFocus();
52764 var btns = this.tb.items.map,
52765 doc = this.editorcore.doc,
52766 frameId = this.editorcore.frameId;
52768 if(!this.disable.font && !Roo.isSafari){
52770 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
52771 if(name != this.fontSelect.dom.value){
52772 this.fontSelect.dom.value = name;
52776 if(!this.disable.format){
52777 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
52778 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
52779 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
52780 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
52782 if(!this.disable.alignments){
52783 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
52784 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
52785 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
52787 if(!Roo.isSafari && !this.disable.lists){
52788 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
52789 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
52792 var ans = this.editorcore.getAllAncestors();
52793 if (this.formatCombo) {
52796 var store = this.formatCombo.store;
52797 this.formatCombo.setValue("");
52798 for (var i =0; i < ans.length;i++) {
52799 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
52801 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
52809 // hides menus... - so this cant be on a menu...
52810 Roo.menu.MenuMgr.hideAll();
52812 //this.editorsyncValue();
52816 createFontOptions : function(){
52817 var buf = [], fs = this.fontFamilies, ff, lc;
52821 for(var i = 0, len = fs.length; i< len; i++){
52823 lc = ff.toLowerCase();
52825 '<option value="',lc,'" style="font-family:',ff,';"',
52826 (this.defaultFont == lc ? ' selected="true">' : '>'),
52831 return buf.join('');
52834 toggleSourceEdit : function(sourceEditMode){
52836 Roo.log("toolbar toogle");
52837 if(sourceEditMode === undefined){
52838 sourceEditMode = !this.sourceEditMode;
52840 this.sourceEditMode = sourceEditMode === true;
52841 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
52842 // just toggle the button?
52843 if(btn.pressed !== this.sourceEditMode){
52844 btn.toggle(this.sourceEditMode);
52848 if(sourceEditMode){
52849 Roo.log("disabling buttons");
52850 this.tb.items.each(function(item){
52851 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
52857 Roo.log("enabling buttons");
52858 if(this.editorcore.initialized){
52859 this.tb.items.each(function(item){
52862 // initialize 'blocks'
52863 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
52864 Roo.htmleditor.Block.factory(e).updateElement(e);
52870 Roo.log("calling toggole on editor");
52871 // tell the editor that it's been pressed..
52872 this.editor.toggleSourceEdit(sourceEditMode);
52876 * Object collection of toolbar tooltips for the buttons in the editor. The key
52877 * is the command id associated with that button and the value is a valid QuickTips object.
52882 title: 'Bold (Ctrl+B)',
52883 text: 'Make the selected text bold.',
52884 cls: 'x-html-editor-tip'
52887 title: 'Italic (Ctrl+I)',
52888 text: 'Make the selected text italic.',
52889 cls: 'x-html-editor-tip'
52897 title: 'Bold (Ctrl+B)',
52898 text: 'Make the selected text bold.',
52899 cls: 'x-html-editor-tip'
52902 title: 'Italic (Ctrl+I)',
52903 text: 'Make the selected text italic.',
52904 cls: 'x-html-editor-tip'
52907 title: 'Underline (Ctrl+U)',
52908 text: 'Underline the selected text.',
52909 cls: 'x-html-editor-tip'
52912 title: 'Strikethrough',
52913 text: 'Strikethrough the selected text.',
52914 cls: 'x-html-editor-tip'
52916 increasefontsize : {
52917 title: 'Grow Text',
52918 text: 'Increase the font size.',
52919 cls: 'x-html-editor-tip'
52921 decreasefontsize : {
52922 title: 'Shrink Text',
52923 text: 'Decrease the font size.',
52924 cls: 'x-html-editor-tip'
52927 title: 'Text Highlight Color',
52928 text: 'Change the background color of the selected text.',
52929 cls: 'x-html-editor-tip'
52932 title: 'Font Color',
52933 text: 'Change the color of the selected text.',
52934 cls: 'x-html-editor-tip'
52937 title: 'Align Text Left',
52938 text: 'Align text to the left.',
52939 cls: 'x-html-editor-tip'
52942 title: 'Center Text',
52943 text: 'Center text in the editor.',
52944 cls: 'x-html-editor-tip'
52947 title: 'Align Text Right',
52948 text: 'Align text to the right.',
52949 cls: 'x-html-editor-tip'
52951 insertunorderedlist : {
52952 title: 'Bullet List',
52953 text: 'Start a bulleted list.',
52954 cls: 'x-html-editor-tip'
52956 insertorderedlist : {
52957 title: 'Numbered List',
52958 text: 'Start a numbered list.',
52959 cls: 'x-html-editor-tip'
52962 title: 'Hyperlink',
52963 text: 'Make the selected text a hyperlink.',
52964 cls: 'x-html-editor-tip'
52967 title: 'Source Edit',
52968 text: 'Switch to source editing mode.',
52969 cls: 'x-html-editor-tip'
52973 onDestroy : function(){
52976 this.tb.items.each(function(item){
52978 item.menu.removeAll();
52980 item.menu.el.destroy();
52988 onFirstFocus: function() {
52989 this.tb.items.each(function(item){
52998 // <script type="text/javascript">
53001 * Ext JS Library 1.1.1
53002 * Copyright(c) 2006-2007, Ext JS, LLC.
53009 * @class Roo.form.HtmlEditor.ToolbarContext
53014 new Roo.form.HtmlEditor({
53017 { xtype: 'ToolbarStandard', styles : {} }
53018 { xtype: 'ToolbarContext', disable : {} }
53024 * @config : {Object} disable List of elements to disable.. (not done yet.)
53025 * @config : {Object} styles Map of styles available.
53029 Roo.form.HtmlEditor.ToolbarContext = function(config)
53032 Roo.apply(this, config);
53033 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
53034 // dont call parent... till later.
53035 this.styles = this.styles || {};
53040 Roo.form.HtmlEditor.ToolbarContext.types = {
53055 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
53081 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
53152 name : 'selectoptions',
53158 // should we really allow this??
53159 // should this just be
53176 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
53177 Roo.form.HtmlEditor.ToolbarContext.stores = false;
53179 Roo.form.HtmlEditor.ToolbarContext.options = {
53181 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
53182 [ 'Courier New', 'Courier New'],
53183 [ 'Tahoma', 'Tahoma'],
53184 [ 'Times New Roman,serif', 'Times'],
53185 [ 'Verdana','Verdana' ]
53189 // fixme - these need to be configurable..
53192 //Roo.form.HtmlEditor.ToolbarContext.types
53195 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
53202 editorcore : false,
53204 * @cfg {Object} disable List of toolbar elements to disable
53209 * @cfg {Object} styles List of styles
53210 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
53212 * These must be defined in the page, so they get rendered correctly..
53223 init : function(editor)
53225 this.editor = editor;
53226 this.editorcore = editor.editorcore ? editor.editorcore : editor;
53227 var editorcore = this.editorcore;
53229 var fid = editorcore.frameId;
53231 function btn(id, toggle, handler){
53232 var xid = fid + '-'+ id ;
53236 cls : 'x-btn-icon x-edit-'+id,
53237 enableToggle:toggle !== false,
53238 scope: editorcore, // was editor...
53239 handler:handler||editorcore.relayBtnCmd,
53240 clickEvent:'mousedown',
53241 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53245 // create a new element.
53246 var wdiv = editor.wrap.createChild({
53248 }, editor.wrap.dom.firstChild.nextSibling, true);
53250 // can we do this more than once??
53252 // stop form submits
53255 // disable everything...
53256 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
53257 this.toolbars = {};
53258 // block toolbars are built in updateToolbar when needed.
53259 for (var i in ty) {
53261 this.toolbars[i] = this.buildToolbar(ty[i],i);
53263 this.tb = this.toolbars.BODY;
53265 this.buildFooter();
53266 this.footer.show();
53267 editor.on('hide', function( ) { this.footer.hide() }, this);
53268 editor.on('show', function( ) { this.footer.show() }, this);
53271 this.rendered = true;
53273 // the all the btns;
53274 editor.on('editorevent', this.updateToolbar, this);
53275 // other toolbars need to implement this..
53276 //editor.on('editmodechange', this.updateToolbar, this);
53282 * Protected method that will not generally be called directly. It triggers
53283 * a toolbar update by reading the markup state of the current selection in the editor.
53285 * Note you can force an update by calling on('editorevent', scope, false)
53287 updateToolbar: function(editor ,ev, sel)
53291 ev.stopEvent(); // se if we can stop this looping with mutiple events.
53295 // capture mouse up - this is handy for selecting images..
53296 // perhaps should go somewhere else...
53297 if(!this.editorcore.activated){
53298 this.editor.onFirstFocus();
53301 //Roo.log(ev ? ev.target : 'NOTARGET');
53304 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
53305 // selectNode - might want to handle IE?
53310 (ev.type == 'mouseup' || ev.type == 'click' ) &&
53311 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
53312 // they have click on an image...
53313 // let's see if we can change the selection...
53316 // this triggers looping?
53317 //this.editorcore.selectNode(sel);
53321 // this forces an id..
53322 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
53323 e.classList.remove('roo-ed-selection');
53325 //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
53326 //Roo.get(node).addClass('roo-ed-selection');
53328 //var updateFooter = sel ? false : true;
53331 var ans = this.editorcore.getAllAncestors();
53334 var ty = Roo.form.HtmlEditor.ToolbarContext.types;
53337 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
53338 sel = sel ? sel : this.editorcore.doc.body;
53339 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
53343 var tn = sel.tagName.toUpperCase();
53344 var lastSel = this.tb.selectedNode;
53345 this.tb.selectedNode = sel;
53346 var left_label = tn;
53348 // ok see if we are editing a block?
53351 // you are not actually selecting the block.
53352 if (sel && sel.hasAttribute('data-block')) {
53354 } else if (sel && sel.closest('[data-block]')) {
53356 db = sel.closest('[data-block]');
53357 //var cepar = sel.closest('[contenteditable=true]');
53358 //if (db && cepar && cepar.tagName != 'BODY') {
53359 // db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
53365 //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
53366 if (db && this.editorcore.enableBlocks) {
53367 block = Roo.htmleditor.Block.factory(db);
53372 db.classList.length > 0 ? db.className + ' ' : ''
53373 ) + 'roo-ed-selection';
53375 // since we removed it earlier... its not there..
53376 tn = 'BLOCK.' + db.getAttribute('data-block');
53378 //this.editorcore.selectNode(db);
53379 if (typeof(this.toolbars[tn]) == 'undefined') {
53380 this.toolbars[tn] = this.buildToolbar( false ,tn ,block.friendly_name, block);
53382 this.toolbars[tn].selectedNode = db;
53383 left_label = block.friendly_name;
53384 ans = this.editorcore.getAllAncestors();
53392 if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
53393 return; // no change?
53399 ///console.log("show: " + tn);
53400 this.tb = typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
53404 this.tb.items.first().el.innerHTML = left_label + ': ';
53407 // update attributes
53408 if (block && this.tb.fields) {
53410 this.tb.fields.each(function(e) {
53411 e.setValue(block[e.name]);
53415 } else if (this.tb.fields && this.tb.selectedNode) {
53416 this.tb.fields.each( function(e) {
53418 e.setValue(this.tb.selectedNode.style[e.stylename]);
53421 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
53423 this.updateToolbarStyles(this.tb.selectedNode);
53428 Roo.menu.MenuMgr.hideAll();
53433 // update the footer
53435 this.updateFooter(ans);
53439 updateToolbarStyles : function(sel)
53441 var hasStyles = false;
53442 for(var i in this.styles) {
53448 if (hasStyles && this.tb.hasStyles) {
53449 var st = this.tb.fields.item(0);
53451 st.store.removeAll();
53452 var cn = sel.className.split(/\s+/);
53455 if (this.styles['*']) {
53457 Roo.each(this.styles['*'], function(v) {
53458 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
53461 if (this.styles[tn]) {
53462 Roo.each(this.styles[tn], function(v) {
53463 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
53467 st.store.loadData(avs);
53474 updateFooter : function(ans)
53477 if (ans === false) {
53478 this.footDisp.dom.innerHTML = '';
53482 this.footerEls = ans.reverse();
53483 Roo.each(this.footerEls, function(a,i) {
53484 if (!a) { return; }
53485 html += html.length ? ' > ' : '';
53487 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
53492 var sz = this.footDisp.up('td').getSize();
53493 this.footDisp.dom.style.width = (sz.width -10) + 'px';
53494 this.footDisp.dom.style.marginLeft = '5px';
53496 this.footDisp.dom.style.overflow = 'hidden';
53498 this.footDisp.dom.innerHTML = html;
53505 onDestroy : function(){
53508 this.tb.items.each(function(item){
53510 item.menu.removeAll();
53512 item.menu.el.destroy();
53520 onFirstFocus: function() {
53521 // need to do this for all the toolbars..
53522 this.tb.items.each(function(item){
53526 buildToolbar: function(tlist, nm, friendly_name, block)
53528 var editor = this.editor;
53529 var editorcore = this.editorcore;
53530 // create a new element.
53531 var wdiv = editor.wrap.createChild({
53533 }, editor.wrap.dom.firstChild.nextSibling, true);
53536 var tb = new Roo.Toolbar(wdiv);
53537 ///this.tb = tb; // << this sets the active toolbar..
53538 if (tlist === false && block) {
53539 tlist = block.contextMenu(this);
53542 tb.hasStyles = false;
53545 tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ": ");
53547 var styles = Array.from(this.styles);
53551 if (styles && styles.length) {
53552 tb.hasStyles = true;
53553 // this needs a multi-select checkbox...
53554 tb.addField( new Roo.form.ComboBox({
53555 store: new Roo.data.SimpleStore({
53557 fields: ['val', 'selected'],
53560 name : '-roo-edit-className',
53561 attrname : 'className',
53562 displayField: 'val',
53566 triggerAction: 'all',
53567 emptyText:'Select Style',
53568 selectOnFocus:true,
53571 'select': function(c, r, i) {
53572 // initial support only for on class per el..
53573 tb.selectedNode.className = r ? r.get('val') : '';
53574 editorcore.syncValue();
53581 var tbc = Roo.form.HtmlEditor.ToolbarContext;
53584 for (var i = 0; i < tlist.length; i++) {
53586 // newer versions will use xtype cfg to create menus.
53587 if (typeof(tlist[i].xtype) != 'undefined') {
53589 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
53595 var item = tlist[i];
53596 tb.add(item.title + ": ");
53599 //optname == used so you can configure the options available..
53600 var opts = item.opts ? item.opts : false;
53601 if (item.optname) { // use the b
53602 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
53607 // opts == pulldown..
53608 tb.addField( new Roo.form.ComboBox({
53609 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
53611 fields: ['val', 'display'],
53614 name : '-roo-edit-' + tlist[i].name,
53616 attrname : tlist[i].name,
53617 stylename : item.style ? item.style : false,
53619 displayField: item.displayField ? item.displayField : 'val',
53620 valueField : 'val',
53622 mode: typeof(tbc.stores[tlist[i].name]) != 'undefined' ? 'remote' : 'local',
53624 triggerAction: 'all',
53625 emptyText:'Select',
53626 selectOnFocus:true,
53627 width: item.width ? item.width : 130,
53629 'select': function(c, r, i) {
53633 tb.selectedNode.style[c.stylename] = r.get('val');
53634 editorcore.syncValue();
53638 tb.selectedNode.removeAttribute(c.attrname);
53639 editorcore.syncValue();
53642 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
53643 editorcore.syncValue();
53652 tb.addField( new Roo.form.TextField({
53655 //allowBlank:false,
53661 tb.addField( new Roo.form.TextField({
53662 name: '-roo-edit-' + tlist[i].name,
53663 attrname : tlist[i].name,
53669 'change' : function(f, nv, ov) {
53672 tb.selectedNode.setAttribute(f.attrname, nv);
53673 editorcore.syncValue();
53681 var show_delete = !block || block.deleteTitle !== false;
53683 show_delete = false;
53687 text: 'Stylesheets',
53690 click : function ()
53692 _this.editor.fireEvent('stylesheetsclick', _this.editor);
53701 text: block && block.deleteTitle ? block.deleteTitle : 'Remove Block or Formating', // remove the tag, and puts the children outside...
53704 click : function ()
53706 var sn = tb.selectedNode;
53708 sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
53714 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
53715 if (sn.hasAttribute('data-block')) {
53716 stn = sn.nextSibling || sn.previousSibling || sn.parentNode;
53717 sn.parentNode.removeChild(sn);
53719 } else if (sn && sn.tagName != 'BODY') {
53720 // remove and keep parents.
53721 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
53726 var range = editorcore.createRange();
53728 range.setStart(stn,0);
53729 range.setEnd(stn,0);
53730 var selection = editorcore.getSelection();
53731 selection.removeAllRanges();
53732 selection.addRange(range);
53735 //_this.updateToolbar(null, null, pn);
53736 _this.updateToolbar(null, null, null);
53737 _this.updateFooter(false);
53748 tb.el.on('click', function(e){
53749 e.preventDefault(); // what does this do?
53751 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
53754 // dont need to disable them... as they will get hidden
53759 buildFooter : function()
53762 var fel = this.editor.wrap.createChild();
53763 this.footer = new Roo.Toolbar(fel);
53764 // toolbar has scrolly on left / right?
53765 var footDisp= new Roo.Toolbar.Fill();
53771 handler : function() {
53772 _t.footDisp.scrollTo('left',0,true)
53776 this.footer.add( footDisp );
53781 handler : function() {
53783 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
53787 var fel = Roo.get(footDisp.el);
53788 fel.addClass('x-editor-context');
53789 this.footDispWrap = fel;
53790 this.footDispWrap.overflow = 'hidden';
53792 this.footDisp = fel.createChild();
53793 this.footDispWrap.on('click', this.onContextClick, this)
53797 // when the footer contect changes
53798 onContextClick : function (ev,dom)
53800 ev.preventDefault();
53801 var cn = dom.className;
53803 if (!cn.match(/x-ed-loc-/)) {
53806 var n = cn.split('-').pop();
53807 var ans = this.footerEls;
53810 this.editorcore.selectNode(sel);
53813 this.updateToolbar(null, null, sel);
53830 * Ext JS Library 1.1.1
53831 * Copyright(c) 2006-2007, Ext JS, LLC.
53833 * Originally Released Under LGPL - original licence link has changed is not relivant.
53836 * <script type="text/javascript">
53840 * @class Roo.form.BasicForm
53841 * @extends Roo.util.Observable
53842 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
53844 * @param {String/HTMLElement/Roo.Element} el The form element or its id
53845 * @param {Object} config Configuration options
53847 Roo.form.BasicForm = function(el, config){
53848 this.allItems = [];
53849 this.childForms = [];
53850 Roo.apply(this, config);
53852 * The Roo.form.Field items in this form.
53853 * @type MixedCollection
53857 this.items = new Roo.util.MixedCollection(false, function(o){
53858 return o.id || (o.id = Roo.id());
53862 * @event beforeaction
53863 * Fires before any action is performed. Return false to cancel the action.
53864 * @param {Form} this
53865 * @param {Action} action The action to be performed
53867 beforeaction: true,
53869 * @event actionfailed
53870 * Fires when an action fails.
53871 * @param {Form} this
53872 * @param {Action} action The action that failed
53874 actionfailed : true,
53876 * @event actioncomplete
53877 * Fires when an action is completed.
53878 * @param {Form} this
53879 * @param {Action} action The action that completed
53881 actioncomplete : true
53886 Roo.form.BasicForm.superclass.constructor.call(this);
53888 Roo.form.BasicForm.popover.apply();
53891 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
53893 * @cfg {String} method
53894 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
53897 * @cfg {DataReader} reader
53898 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
53899 * This is optional as there is built-in support for processing JSON.
53902 * @cfg {DataReader} errorReader
53903 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
53904 * This is completely optional as there is built-in support for processing JSON.
53907 * @cfg {String} url
53908 * The URL to use for form actions if one isn't supplied in the action options.
53911 * @cfg {Boolean} fileUpload
53912 * Set to true if this form is a file upload.
53916 * @cfg {Object} baseParams
53917 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
53922 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
53927 activeAction : null,
53930 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
53931 * or setValues() data instead of when the form was first created.
53933 trackResetOnLoad : false,
53937 * childForms - used for multi-tab forms
53940 childForms : false,
53943 * allItems - full list of fields.
53949 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
53950 * element by passing it or its id or mask the form itself by passing in true.
53953 waitMsgTarget : false,
53958 disableMask : false,
53961 * @cfg {Boolean} errorMask (true|false) default false
53966 * @cfg {Number} maskOffset Default 100
53971 initEl : function(el){
53972 this.el = Roo.get(el);
53973 this.id = this.el.id || Roo.id();
53974 this.el.on('submit', this.onSubmit, this);
53975 this.el.addClass('x-form');
53979 onSubmit : function(e){
53984 * Returns true if client-side validation on the form is successful.
53987 isValid : function(){
53989 var target = false;
53990 this.items.each(function(f){
53997 if(!target && f.el.isVisible(true)){
54002 if(this.errorMask && !valid){
54003 Roo.form.BasicForm.popover.mask(this, target);
54009 * Returns array of invalid form fields.
54013 invalidFields : function()
54016 this.items.each(function(f){
54029 * DEPRICATED Returns true if any fields in this form have changed since their original load.
54032 isDirty : function(){
54034 this.items.each(function(f){
54044 * Returns true if any fields in this form have changed since their original load. (New version)
54048 hasChanged : function()
54051 this.items.each(function(f){
54052 if(f.hasChanged()){
54061 * Resets all hasChanged to 'false' -
54062 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
54063 * So hasChanged storage is only to be used for this purpose
54066 resetHasChanged : function()
54068 this.items.each(function(f){
54069 f.resetHasChanged();
54076 * Performs a predefined action (submit or load) or custom actions you define on this form.
54077 * @param {String} actionName The name of the action type
54078 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
54079 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
54080 * accept other config options):
54082 Property Type Description
54083 ---------------- --------------- ----------------------------------------------------------------------------------
54084 url String The url for the action (defaults to the form's url)
54085 method String The form method to use (defaults to the form's method, or POST if not defined)
54086 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
54087 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
54088 validate the form on the client (defaults to false)
54090 * @return {BasicForm} this
54092 doAction : function(action, options){
54093 if(typeof action == 'string'){
54094 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
54096 if(this.fireEvent('beforeaction', this, action) !== false){
54097 this.beforeAction(action);
54098 action.run.defer(100, action);
54104 * Shortcut to do a submit action.
54105 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
54106 * @return {BasicForm} this
54108 submit : function(options){
54109 this.doAction('submit', options);
54114 * Shortcut to do a load action.
54115 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
54116 * @return {BasicForm} this
54118 load : function(options){
54119 this.doAction('load', options);
54124 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
54125 * @param {Record} record The record to edit
54126 * @return {BasicForm} this
54128 updateRecord : function(record){
54129 record.beginEdit();
54130 var fs = record.fields;
54131 fs.each(function(f){
54132 var field = this.findField(f.name);
54134 record.set(f.name, field.getValue());
54142 * Loads an Roo.data.Record into this form.
54143 * @param {Record} record The record to load
54144 * @return {BasicForm} this
54146 loadRecord : function(record){
54147 this.setValues(record.data);
54152 beforeAction : function(action){
54153 var o = action.options;
54155 if(!this.disableMask) {
54156 if(this.waitMsgTarget === true){
54157 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
54158 }else if(this.waitMsgTarget){
54159 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
54160 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
54162 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
54170 afterAction : function(action, success){
54171 this.activeAction = null;
54172 var o = action.options;
54174 if(!this.disableMask) {
54175 if(this.waitMsgTarget === true){
54177 }else if(this.waitMsgTarget){
54178 this.waitMsgTarget.unmask();
54180 Roo.MessageBox.updateProgress(1);
54181 Roo.MessageBox.hide();
54189 Roo.callback(o.success, o.scope, [this, action]);
54190 this.fireEvent('actioncomplete', this, action);
54194 // failure condition..
54195 // we have a scenario where updates need confirming.
54196 // eg. if a locking scenario exists..
54197 // we look for { errors : { needs_confirm : true }} in the response.
54199 (typeof(action.result) != 'undefined') &&
54200 (typeof(action.result.errors) != 'undefined') &&
54201 (typeof(action.result.errors.needs_confirm) != 'undefined')
54204 Roo.MessageBox.confirm(
54205 "Change requires confirmation",
54206 action.result.errorMsg,
54211 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
54221 Roo.callback(o.failure, o.scope, [this, action]);
54222 // show an error message if no failed handler is set..
54223 if (!this.hasListener('actionfailed')) {
54224 Roo.MessageBox.alert("Error",
54225 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
54226 action.result.errorMsg :
54227 "Saving Failed, please check your entries or try again"
54231 this.fireEvent('actionfailed', this, action);
54237 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
54238 * @param {String} id The value to search for
54241 findField : function(id){
54242 var field = this.items.get(id);
54244 this.items.each(function(f){
54245 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
54251 return field || null;
54255 * Add a secondary form to this one,
54256 * Used to provide tabbed forms. One form is primary, with hidden values
54257 * which mirror the elements from the other forms.
54259 * @param {Roo.form.Form} form to add.
54262 addForm : function(form)
54265 if (this.childForms.indexOf(form) > -1) {
54269 this.childForms.push(form);
54271 Roo.each(form.allItems, function (fe) {
54273 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
54274 if (this.findField(n)) { // already added..
54277 var add = new Roo.form.Hidden({
54280 add.render(this.el);
54287 * Mark fields in this form invalid in bulk.
54288 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
54289 * @return {BasicForm} this
54291 markInvalid : function(errors){
54292 if(errors instanceof Array){
54293 for(var i = 0, len = errors.length; i < len; i++){
54294 var fieldError = errors[i];
54295 var f = this.findField(fieldError.id);
54297 f.markInvalid(fieldError.msg);
54303 if(typeof errors[id] != 'function' && (field = this.findField(id))){
54304 field.markInvalid(errors[id]);
54308 Roo.each(this.childForms || [], function (f) {
54309 f.markInvalid(errors);
54316 * Set values for fields in this form in bulk.
54317 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
54318 * @return {BasicForm} this
54320 setValues : function(values){
54321 if(values instanceof Array){ // array of objects
54322 for(var i = 0, len = values.length; i < len; i++){
54324 var f = this.findField(v.id);
54326 f.setValue(v.value);
54327 if(this.trackResetOnLoad){
54328 f.originalValue = f.getValue();
54332 }else{ // object hash
54335 if(typeof values[id] != 'function' && (field = this.findField(id))){
54340 if (field.setFromData &&
54341 field.valueField &&
54342 field.displayField &&
54343 // combos' with local stores can
54344 // be queried via setValue()
54345 // to set their value..
54346 (field.store && !field.store.isLocal)
54350 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
54351 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
54352 field.setFromData(sd);
54354 } else if (field.inputType && field.inputType == 'radio') {
54356 field.setValue(values[id]);
54358 field.setValue(values[id]);
54362 if(this.trackResetOnLoad){
54363 field.originalValue = field.getValue();
54368 this.resetHasChanged();
54371 Roo.each(this.childForms || [], function (f) {
54372 f.setValues(values);
54373 f.resetHasChanged();
54380 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
54381 * they are returned as an array.
54382 * @param {Boolean} asString (def)
54385 getValues : function(asString)
54387 if (this.childForms) {
54388 // copy values from the child forms
54389 Roo.each(this.childForms, function (f) {
54390 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
54395 if (typeof(FormData) != 'undefined' && asString !== true) {
54396 // this relies on a 'recent' version of chrome apparently...
54398 var fd = (new FormData(this.el.dom)).entries();
54400 var ent = fd.next();
54401 while (!ent.done) {
54402 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
54413 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
54414 if(asString === true){
54417 return Roo.urlDecode(fs);
54421 * Returns the fields in this form as an object with key/value pairs.
54422 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
54423 * Normally this will not return readOnly data
54424 * @param {Boolean} with_readonly return readonly field data.
54427 getFieldValues : function(with_readonly)
54429 if (this.childForms) {
54430 // copy values from the child forms
54431 // should this call getFieldValues - probably not as we do not currently copy
54432 // hidden fields when we generate..
54433 Roo.each(this.childForms, function (f) {
54434 this.setValues(f.getFieldValues());
54439 this.items.each(function(f){
54441 if (f.readOnly && with_readonly !== true) {
54442 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
54443 // if a subform contains a copy of them.
54444 // if you have subforms with the same editable data, you will need to copy the data back
54448 if (!f.getName()) {
54451 var v = f.getValue();
54452 if (f.inputType =='radio') {
54453 if (typeof(ret[f.getName()]) == 'undefined') {
54454 ret[f.getName()] = ''; // empty..
54457 if (!f.el.dom.checked) {
54461 v = f.el.dom.value;
54465 // not sure if this supported any more..
54466 if ((typeof(v) == 'object') && f.getRawValue) {
54467 v = f.getRawValue() ; // dates..
54469 // combo boxes where name != hiddenName...
54470 if (f.name != f.getName()) {
54471 ret[f.name] = f.getRawValue();
54473 ret[f.getName()] = v;
54480 * Clears all invalid messages in this form.
54481 * @return {BasicForm} this
54483 clearInvalid : function(){
54484 this.items.each(function(f){
54488 Roo.each(this.childForms || [], function (f) {
54497 * Resets this form.
54498 * @return {BasicForm} this
54500 reset : function(){
54501 this.items.each(function(f){
54505 Roo.each(this.childForms || [], function (f) {
54508 this.resetHasChanged();
54514 * Add Roo.form components to this form.
54515 * @param {Field} field1
54516 * @param {Field} field2 (optional)
54517 * @param {Field} etc (optional)
54518 * @return {BasicForm} this
54521 this.items.addAll(Array.prototype.slice.call(arguments, 0));
54527 * Removes a field from the items collection (does NOT remove its markup).
54528 * @param {Field} field
54529 * @return {BasicForm} this
54531 remove : function(field){
54532 this.items.remove(field);
54537 * Looks at the fields in this form, checks them for an id attribute,
54538 * and calls applyTo on the existing dom element with that id.
54539 * @return {BasicForm} this
54541 render : function(){
54542 this.items.each(function(f){
54543 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
54551 * Calls {@link Ext#apply} for all fields in this form with the passed object.
54552 * @param {Object} values
54553 * @return {BasicForm} this
54555 applyToFields : function(o){
54556 this.items.each(function(f){
54563 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
54564 * @param {Object} values
54565 * @return {BasicForm} this
54567 applyIfToFields : function(o){
54568 this.items.each(function(f){
54576 Roo.BasicForm = Roo.form.BasicForm;
54578 Roo.apply(Roo.form.BasicForm, {
54592 intervalID : false,
54598 if(this.isApplied){
54603 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
54604 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
54605 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
54606 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
54609 this.maskEl.top.enableDisplayMode("block");
54610 this.maskEl.left.enableDisplayMode("block");
54611 this.maskEl.bottom.enableDisplayMode("block");
54612 this.maskEl.right.enableDisplayMode("block");
54614 Roo.get(document.body).on('click', function(){
54618 Roo.get(document.body).on('touchstart', function(){
54622 this.isApplied = true
54625 mask : function(form, target)
54629 this.target = target;
54631 if(!this.form.errorMask || !target.el){
54635 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
54637 var ot = this.target.el.calcOffsetsTo(scrollable);
54639 var scrollTo = ot[1] - this.form.maskOffset;
54641 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
54643 scrollable.scrollTo('top', scrollTo);
54645 var el = this.target.wrap || this.target.el;
54647 var box = el.getBox();
54649 this.maskEl.top.setStyle('position', 'absolute');
54650 this.maskEl.top.setStyle('z-index', 10000);
54651 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
54652 this.maskEl.top.setLeft(0);
54653 this.maskEl.top.setTop(0);
54654 this.maskEl.top.show();
54656 this.maskEl.left.setStyle('position', 'absolute');
54657 this.maskEl.left.setStyle('z-index', 10000);
54658 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
54659 this.maskEl.left.setLeft(0);
54660 this.maskEl.left.setTop(box.y - this.padding);
54661 this.maskEl.left.show();
54663 this.maskEl.bottom.setStyle('position', 'absolute');
54664 this.maskEl.bottom.setStyle('z-index', 10000);
54665 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
54666 this.maskEl.bottom.setLeft(0);
54667 this.maskEl.bottom.setTop(box.bottom + this.padding);
54668 this.maskEl.bottom.show();
54670 this.maskEl.right.setStyle('position', 'absolute');
54671 this.maskEl.right.setStyle('z-index', 10000);
54672 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
54673 this.maskEl.right.setLeft(box.right + this.padding);
54674 this.maskEl.right.setTop(box.y - this.padding);
54675 this.maskEl.right.show();
54677 this.intervalID = window.setInterval(function() {
54678 Roo.form.BasicForm.popover.unmask();
54681 window.onwheel = function(){ return false;};
54683 (function(){ this.isMasked = true; }).defer(500, this);
54687 unmask : function()
54689 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
54693 this.maskEl.top.setStyle('position', 'absolute');
54694 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
54695 this.maskEl.top.hide();
54697 this.maskEl.left.setStyle('position', 'absolute');
54698 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
54699 this.maskEl.left.hide();
54701 this.maskEl.bottom.setStyle('position', 'absolute');
54702 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
54703 this.maskEl.bottom.hide();
54705 this.maskEl.right.setStyle('position', 'absolute');
54706 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
54707 this.maskEl.right.hide();
54709 window.onwheel = function(){ return true;};
54711 if(this.intervalID){
54712 window.clearInterval(this.intervalID);
54713 this.intervalID = false;
54716 this.isMasked = false;
54724 * Ext JS Library 1.1.1
54725 * Copyright(c) 2006-2007, Ext JS, LLC.
54727 * Originally Released Under LGPL - original licence link has changed is not relivant.
54730 * <script type="text/javascript">
54734 * @class Roo.form.Form
54735 * @extends Roo.form.BasicForm
54736 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
54737 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
54739 * @param {Object} config Configuration options
54741 Roo.form.Form = function(config){
54743 if (config.items) {
54744 xitems = config.items;
54745 delete config.items;
54749 Roo.form.Form.superclass.constructor.call(this, null, config);
54750 this.url = this.url || this.action;
54752 this.root = new Roo.form.Layout(Roo.applyIf({
54756 this.active = this.root;
54758 * Array of all the buttons that have been added to this form via {@link addButton}
54762 this.allItems = [];
54765 * @event clientvalidation
54766 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
54767 * @param {Form} this
54768 * @param {Boolean} valid true if the form has passed client-side validation
54770 clientvalidation: true,
54773 * Fires when the form is rendered
54774 * @param {Roo.form.Form} form
54779 if (this.progressUrl) {
54780 // push a hidden field onto the list of fields..
54784 name : 'UPLOAD_IDENTIFIER'
54789 Roo.each(xitems, this.addxtype, this);
54793 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
54795 * @cfg {Roo.Button} buttons[] buttons at bottom of form
54799 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
54802 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
54805 * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
54807 buttonAlign:'center',
54810 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
54815 * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
54816 * This property cascades to child containers if not set.
54821 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
54822 * fires a looping event with that state. This is required to bind buttons to the valid
54823 * state using the config value formBind:true on the button.
54825 monitorValid : false,
54828 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
54833 * @cfg {String} progressUrl - Url to return progress data
54836 progressUrl : false,
54838 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
54839 * sending a formdata with extra parameters - eg uploaded elements.
54845 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
54846 * fields are added and the column is closed. If no fields are passed the column remains open
54847 * until end() is called.
54848 * @param {Object} config The config to pass to the column
54849 * @param {Field} field1 (optional)
54850 * @param {Field} field2 (optional)
54851 * @param {Field} etc (optional)
54852 * @return Column The column container object
54854 column : function(c){
54855 var col = new Roo.form.Column(c);
54857 if(arguments.length > 1){ // duplicate code required because of Opera
54858 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54865 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
54866 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
54867 * until end() is called.
54868 * @param {Object} config The config to pass to the fieldset
54869 * @param {Field} field1 (optional)
54870 * @param {Field} field2 (optional)
54871 * @param {Field} etc (optional)
54872 * @return FieldSet The fieldset container object
54874 fieldset : function(c){
54875 var fs = new Roo.form.FieldSet(c);
54877 if(arguments.length > 1){ // duplicate code required because of Opera
54878 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54885 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
54886 * fields are added and the container is closed. If no fields are passed the container remains open
54887 * until end() is called.
54888 * @param {Object} config The config to pass to the Layout
54889 * @param {Field} field1 (optional)
54890 * @param {Field} field2 (optional)
54891 * @param {Field} etc (optional)
54892 * @return Layout The container object
54894 container : function(c){
54895 var l = new Roo.form.Layout(c);
54897 if(arguments.length > 1){ // duplicate code required because of Opera
54898 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54905 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
54906 * @param {Object} container A Roo.form.Layout or subclass of Layout
54907 * @return {Form} this
54909 start : function(c){
54910 // cascade label info
54911 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
54912 this.active.stack.push(c);
54913 c.ownerCt = this.active;
54919 * Closes the current open container
54920 * @return {Form} this
54923 if(this.active == this.root){
54926 this.active = this.active.ownerCt;
54931 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
54932 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
54933 * as the label of the field.
54934 * @param {Field} field1
54935 * @param {Field} field2 (optional)
54936 * @param {Field} etc. (optional)
54937 * @return {Form} this
54940 this.active.stack.push.apply(this.active.stack, arguments);
54941 this.allItems.push.apply(this.allItems,arguments);
54943 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
54944 if(a[i].isFormField){
54949 Roo.form.Form.superclass.add.apply(this, r);
54959 * Find any element that has been added to a form, using it's ID or name
54960 * This can include framesets, columns etc. along with regular fields..
54961 * @param {String} id - id or name to find.
54963 * @return {Element} e - or false if nothing found.
54965 findbyId : function(id)
54971 Roo.each(this.allItems, function(f){
54972 if (f.id == id || f.name == id ){
54983 * Render this form into the passed container. This should only be called once!
54984 * @param {String/HTMLElement/Element} container The element this component should be rendered into
54985 * @return {Form} this
54987 render : function(ct)
54993 var o = this.autoCreate || {
54995 method : this.method || 'POST',
54996 id : this.id || Roo.id()
54998 this.initEl(ct.createChild(o));
55000 this.root.render(this.el);
55004 this.items.each(function(f){
55005 f.render('x-form-el-'+f.id);
55008 if(this.buttons.length > 0){
55009 // tables are required to maintain order and for correct IE layout
55010 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
55011 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
55012 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
55014 var tr = tb.getElementsByTagName('tr')[0];
55015 for(var i = 0, len = this.buttons.length; i < len; i++) {
55016 var b = this.buttons[i];
55017 var td = document.createElement('td');
55018 td.className = 'x-form-btn-td';
55019 b.render(tr.appendChild(td));
55022 if(this.monitorValid){ // initialize after render
55023 this.startMonitoring();
55025 this.fireEvent('rendered', this);
55030 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
55031 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
55032 * object or a valid Roo.DomHelper element config
55033 * @param {Function} handler The function called when the button is clicked
55034 * @param {Object} scope (optional) The scope of the handler function
55035 * @return {Roo.Button}
55037 addButton : function(config, handler, scope){
55041 minWidth: this.minButtonWidth,
55044 if(typeof config == "string"){
55047 Roo.apply(bc, config);
55049 var btn = new Roo.Button(null, bc);
55050 this.buttons.push(btn);
55055 * Adds a series of form elements (using the xtype property as the factory method.
55056 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
55057 * @param {Object} config
55060 addxtype : function()
55062 var ar = Array.prototype.slice.call(arguments, 0);
55064 for(var i = 0; i < ar.length; i++) {
55066 continue; // skip -- if this happends something invalid got sent, we
55067 // should ignore it, as basically that interface element will not show up
55068 // and that should be pretty obvious!!
55071 if (Roo.form[ar[i].xtype]) {
55073 var fe = Roo.factory(ar[i], Roo.form);
55079 fe.store.form = this;
55084 this.allItems.push(fe);
55085 if (fe.items && fe.addxtype) {
55086 fe.addxtype.apply(fe, fe.items);
55096 // console.log('adding ' + ar[i].xtype);
55098 if (ar[i].xtype == 'Button') {
55099 //console.log('adding button');
55100 //console.log(ar[i]);
55101 this.addButton(ar[i]);
55102 this.allItems.push(fe);
55106 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
55107 alert('end is not supported on xtype any more, use items');
55109 // //console.log('adding end');
55117 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
55118 * option "monitorValid"
55120 startMonitoring : function(){
55123 Roo.TaskMgr.start({
55124 run : this.bindHandler,
55125 interval : this.monitorPoll || 200,
55132 * Stops monitoring of the valid state of this form
55134 stopMonitoring : function(){
55135 this.bound = false;
55139 bindHandler : function(){
55141 return false; // stops binding
55144 this.items.each(function(f){
55145 if(!f.isValid(true)){
55150 for(var i = 0, len = this.buttons.length; i < len; i++){
55151 var btn = this.buttons[i];
55152 if(btn.formBind === true && btn.disabled === valid){
55153 btn.setDisabled(!valid);
55156 this.fireEvent('clientvalidation', this, valid);
55170 Roo.Form = Roo.form.Form;
55173 * Ext JS Library 1.1.1
55174 * Copyright(c) 2006-2007, Ext JS, LLC.
55176 * Originally Released Under LGPL - original licence link has changed is not relivant.
55179 * <script type="text/javascript">
55182 // as we use this in bootstrap.
55183 Roo.namespace('Roo.form');
55185 * @class Roo.form.Action
55186 * Internal Class used to handle form actions
55188 * @param {Roo.form.BasicForm} el The form element or its id
55189 * @param {Object} config Configuration options
55194 // define the action interface
55195 Roo.form.Action = function(form, options){
55197 this.options = options || {};
55200 * Client Validation Failed
55203 Roo.form.Action.CLIENT_INVALID = 'client';
55205 * Server Validation Failed
55208 Roo.form.Action.SERVER_INVALID = 'server';
55210 * Connect to Server Failed
55213 Roo.form.Action.CONNECT_FAILURE = 'connect';
55215 * Reading Data from Server Failed
55218 Roo.form.Action.LOAD_FAILURE = 'load';
55220 Roo.form.Action.prototype = {
55222 failureType : undefined,
55223 response : undefined,
55224 result : undefined,
55226 // interface method
55227 run : function(options){
55231 // interface method
55232 success : function(response){
55236 // interface method
55237 handleResponse : function(response){
55241 // default connection failure
55242 failure : function(response){
55244 this.response = response;
55245 this.failureType = Roo.form.Action.CONNECT_FAILURE;
55246 this.form.afterAction(this, false);
55249 processResponse : function(response){
55250 this.response = response;
55251 if(!response.responseText){
55254 this.result = this.handleResponse(response);
55255 return this.result;
55258 // utility functions used internally
55259 getUrl : function(appendParams){
55260 var url = this.options.url || this.form.url || this.form.el.dom.action;
55262 var p = this.getParams();
55264 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
55270 getMethod : function(){
55271 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
55274 getParams : function(){
55275 var bp = this.form.baseParams;
55276 var p = this.options.params;
55278 if(typeof p == "object"){
55279 p = Roo.urlEncode(Roo.applyIf(p, bp));
55280 }else if(typeof p == 'string' && bp){
55281 p += '&' + Roo.urlEncode(bp);
55284 p = Roo.urlEncode(bp);
55289 createCallback : function(){
55291 success: this.success,
55292 failure: this.failure,
55294 timeout: (this.form.timeout*1000),
55295 upload: this.form.fileUpload ? this.success : undefined
55300 Roo.form.Action.Submit = function(form, options){
55301 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
55304 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
55307 haveProgress : false,
55308 uploadComplete : false,
55310 // uploadProgress indicator.
55311 uploadProgress : function()
55313 if (!this.form.progressUrl) {
55317 if (!this.haveProgress) {
55318 Roo.MessageBox.progress("Uploading", "Uploading");
55320 if (this.uploadComplete) {
55321 Roo.MessageBox.hide();
55325 this.haveProgress = true;
55327 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
55329 var c = new Roo.data.Connection();
55331 url : this.form.progressUrl,
55336 success : function(req){
55337 //console.log(data);
55341 rdata = Roo.decode(req.responseText)
55343 Roo.log("Invalid data from server..");
55347 if (!rdata || !rdata.success) {
55349 Roo.MessageBox.alert(Roo.encode(rdata));
55352 var data = rdata.data;
55354 if (this.uploadComplete) {
55355 Roo.MessageBox.hide();
55360 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
55361 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
55364 this.uploadProgress.defer(2000,this);
55367 failure: function(data) {
55368 Roo.log('progress url failed ');
55379 // run get Values on the form, so it syncs any secondary forms.
55380 this.form.getValues();
55382 var o = this.options;
55383 var method = this.getMethod();
55384 var isPost = method == 'POST';
55385 if(o.clientValidation === false || this.form.isValid()){
55387 if (this.form.progressUrl) {
55388 this.form.findField('UPLOAD_IDENTIFIER').setValue(
55389 (new Date() * 1) + '' + Math.random());
55394 Roo.Ajax.request(Roo.apply(this.createCallback(), {
55395 form:this.form.el.dom,
55396 url:this.getUrl(!isPost),
55398 params:isPost ? this.getParams() : null,
55399 isUpload: this.form.fileUpload,
55400 formData : this.form.formData
55403 this.uploadProgress();
55405 }else if (o.clientValidation !== false){ // client validation failed
55406 this.failureType = Roo.form.Action.CLIENT_INVALID;
55407 this.form.afterAction(this, false);
55411 success : function(response)
55413 this.uploadComplete= true;
55414 if (this.haveProgress) {
55415 Roo.MessageBox.hide();
55419 var result = this.processResponse(response);
55420 if(result === true || result.success){
55421 this.form.afterAction(this, true);
55425 this.form.markInvalid(result.errors);
55426 this.failureType = Roo.form.Action.SERVER_INVALID;
55428 this.form.afterAction(this, false);
55430 failure : function(response)
55432 this.uploadComplete= true;
55433 if (this.haveProgress) {
55434 Roo.MessageBox.hide();
55437 this.response = response;
55438 this.failureType = Roo.form.Action.CONNECT_FAILURE;
55439 this.form.afterAction(this, false);
55442 handleResponse : function(response){
55443 if(this.form.errorReader){
55444 var rs = this.form.errorReader.read(response);
55447 for(var i = 0, len = rs.records.length; i < len; i++) {
55448 var r = rs.records[i];
55449 errors[i] = r.data;
55452 if(errors.length < 1){
55456 success : rs.success,
55462 var rt = response.responseText;
55463 if (rt.match(/^\<!--\[CDATA\[/)) {
55464 rt = rt.replace(/^\<!--\[CDATA\[/,'');
55465 rt = rt.replace(/\]\]--\>$/,'');
55468 ret = Roo.decode(rt);
55472 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
55482 Roo.form.Action.Load = function(form, options){
55483 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
55484 this.reader = this.form.reader;
55487 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
55492 Roo.Ajax.request(Roo.apply(
55493 this.createCallback(), {
55494 method:this.getMethod(),
55495 url:this.getUrl(false),
55496 params:this.getParams()
55500 success : function(response){
55502 var result = this.processResponse(response);
55503 if(result === true || !result.success || !result.data){
55504 this.failureType = Roo.form.Action.LOAD_FAILURE;
55505 this.form.afterAction(this, false);
55508 this.form.clearInvalid();
55509 this.form.setValues(result.data);
55510 this.form.afterAction(this, true);
55513 handleResponse : function(response){
55514 if(this.form.reader){
55515 var rs = this.form.reader.read(response);
55516 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
55518 success : rs.success,
55522 return Roo.decode(response.responseText);
55526 Roo.form.Action.ACTION_TYPES = {
55527 'load' : Roo.form.Action.Load,
55528 'submit' : Roo.form.Action.Submit
55531 * Ext JS Library 1.1.1
55532 * Copyright(c) 2006-2007, Ext JS, LLC.
55534 * Originally Released Under LGPL - original licence link has changed is not relivant.
55537 * <script type="text/javascript">
55541 * @class Roo.form.Layout
55542 * @extends Roo.Component
55543 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55544 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
55546 * @param {Object} config Configuration options
55548 Roo.form.Layout = function(config){
55550 if (config.items) {
55551 xitems = config.items;
55552 delete config.items;
55554 Roo.form.Layout.superclass.constructor.call(this, config);
55556 Roo.each(xitems, this.addxtype, this);
55560 Roo.extend(Roo.form.Layout, Roo.Component, {
55562 * @cfg {String/Object} autoCreate
55563 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
55566 * @cfg {String/Object/Function} style
55567 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
55568 * a function which returns such a specification.
55571 * @cfg {String} labelAlign (left|top|right)
55572 * Valid values are "left," "top" and "right" (defaults to "left")
55575 * @cfg {Number} labelWidth
55576 * Fixed width in pixels of all field labels (defaults to undefined)
55579 * @cfg {Boolean} clear
55580 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
55584 * @cfg {String} labelSeparator
55585 * The separator to use after field labels (defaults to ':')
55587 labelSeparator : ':',
55589 * @cfg {Boolean} hideLabels
55590 * True to suppress the display of field labels in this layout (defaults to false)
55592 hideLabels : false,
55595 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
55600 onRender : function(ct, position){
55601 if(this.el){ // from markup
55602 this.el = Roo.get(this.el);
55603 }else { // generate
55604 var cfg = this.getAutoCreate();
55605 this.el = ct.createChild(cfg, position);
55608 this.el.applyStyles(this.style);
55610 if(this.labelAlign){
55611 this.el.addClass('x-form-label-'+this.labelAlign);
55613 if(this.hideLabels){
55614 this.labelStyle = "display:none";
55615 this.elementStyle = "padding-left:0;";
55617 if(typeof this.labelWidth == 'number'){
55618 this.labelStyle = "width:"+this.labelWidth+"px;";
55619 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
55621 if(this.labelAlign == 'top'){
55622 this.labelStyle = "width:auto;";
55623 this.elementStyle = "padding-left:0;";
55626 var stack = this.stack;
55627 var slen = stack.length;
55629 if(!this.fieldTpl){
55630 var t = new Roo.Template(
55631 '<div class="x-form-item {5}">',
55632 '<label for="{0}" style="{2}">{1}{4}</label>',
55633 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55635 '</div><div class="x-form-clear-left"></div>'
55637 t.disableFormats = true;
55639 Roo.form.Layout.prototype.fieldTpl = t;
55641 for(var i = 0; i < slen; i++) {
55642 if(stack[i].isFormField){
55643 this.renderField(stack[i]);
55645 this.renderComponent(stack[i]);
55650 this.el.createChild({cls:'x-form-clear'});
55655 renderField : function(f){
55656 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
55659 f.labelStyle||this.labelStyle||'', //2
55660 this.elementStyle||'', //3
55661 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
55662 f.itemCls||this.itemCls||'' //5
55663 ], true).getPrevSibling());
55667 renderComponent : function(c){
55668 c.render(c.isLayout ? this.el : this.el.createChild());
55671 * Adds a object form elements (using the xtype property as the factory method.)
55672 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
55673 * @param {Object} config
55675 addxtype : function(o)
55677 // create the lement.
55678 o.form = this.form;
55679 var fe = Roo.factory(o, Roo.form);
55680 this.form.allItems.push(fe);
55681 this.stack.push(fe);
55683 if (fe.isFormField) {
55684 this.form.items.add(fe);
55693 * @class Roo.form.Column
55694 * @extends Roo.form.Layout
55695 * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55696 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
55698 * @param {Object} config Configuration options
55700 Roo.form.Column = function(config){
55701 Roo.form.Column.superclass.constructor.call(this, config);
55704 Roo.extend(Roo.form.Column, Roo.form.Layout, {
55706 * @cfg {Number/String} width
55707 * The fixed width of the column in pixels or CSS value (defaults to "auto")
55710 * @cfg {String/Object} autoCreate
55711 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
55715 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
55718 onRender : function(ct, position){
55719 Roo.form.Column.superclass.onRender.call(this, ct, position);
55721 this.el.setWidth(this.width);
55727 * @class Roo.form.Row
55728 * @extends Roo.form.Layout
55729 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55730 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
55732 * @param {Object} config Configuration options
55736 Roo.form.Row = function(config){
55737 Roo.form.Row.superclass.constructor.call(this, config);
55740 Roo.extend(Roo.form.Row, Roo.form.Layout, {
55742 * @cfg {Number/String} width
55743 * The fixed width of the column in pixels or CSS value (defaults to "auto")
55746 * @cfg {Number/String} height
55747 * The fixed height of the column in pixels or CSS value (defaults to "auto")
55749 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
55753 onRender : function(ct, position){
55754 //console.log('row render');
55756 var t = new Roo.Template(
55757 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
55758 '<label for="{0}" style="{2}">{1}{4}</label>',
55759 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55763 t.disableFormats = true;
55765 Roo.form.Layout.prototype.rowTpl = t;
55767 this.fieldTpl = this.rowTpl;
55769 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
55770 var labelWidth = 100;
55772 if ((this.labelAlign != 'top')) {
55773 if (typeof this.labelWidth == 'number') {
55774 labelWidth = this.labelWidth
55776 this.padWidth = 20 + labelWidth;
55780 Roo.form.Column.superclass.onRender.call(this, ct, position);
55782 this.el.setWidth(this.width);
55785 this.el.setHeight(this.height);
55790 renderField : function(f){
55791 f.fieldEl = this.fieldTpl.append(this.el, [
55792 f.id, f.fieldLabel,
55793 f.labelStyle||this.labelStyle||'',
55794 this.elementStyle||'',
55795 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
55796 f.itemCls||this.itemCls||'',
55797 f.width ? f.width + this.padWidth : 160 + this.padWidth
55804 * @class Roo.form.FieldSet
55805 * @extends Roo.form.Layout
55806 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
55807 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
55809 * @param {Object} config Configuration options
55811 Roo.form.FieldSet = function(config){
55812 Roo.form.FieldSet.superclass.constructor.call(this, config);
55815 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
55817 * @cfg {String} legend
55818 * The text to display as the legend for the FieldSet (defaults to '')
55821 * @cfg {String/Object} autoCreate
55822 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
55826 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
55829 onRender : function(ct, position){
55830 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
55832 this.setLegend(this.legend);
55837 setLegend : function(text){
55839 this.el.child('legend').update(text);
55844 * Ext JS Library 1.1.1
55845 * Copyright(c) 2006-2007, Ext JS, LLC.
55847 * Originally Released Under LGPL - original licence link has changed is not relivant.
55850 * <script type="text/javascript">
55853 * @class Roo.form.VTypes
55854 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
55857 Roo.form.VTypes = function(){
55858 // closure these in so they are only created once.
55859 var alpha = /^[a-zA-Z_]+$/;
55860 var alphanum = /^[a-zA-Z0-9_]+$/;
55861 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
55862 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
55864 // All these messages and functions are configurable
55867 * The function used to validate email addresses
55868 * @param {String} value The email address
55870 email : function(v){
55871 return email.test(v);
55874 * The error text to display when the email validation function returns false
55877 emailText : 'This field should be an e-mail address in the format "user@domain.com"',
55879 * The keystroke filter mask to be applied on email input
55882 emailMask : /[a-z0-9_\.\-@]/i,
55885 * The function used to validate URLs
55886 * @param {String} value The URL
55889 return url.test(v);
55892 * The error text to display when the url validation function returns false
55895 urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
55898 * The function used to validate alpha values
55899 * @param {String} value The value
55901 alpha : function(v){
55902 return alpha.test(v);
55905 * The error text to display when the alpha validation function returns false
55908 alphaText : 'This field should only contain letters and _',
55910 * The keystroke filter mask to be applied on alpha input
55913 alphaMask : /[a-z_]/i,
55916 * The function used to validate alphanumeric values
55917 * @param {String} value The value
55919 alphanum : function(v){
55920 return alphanum.test(v);
55923 * The error text to display when the alphanumeric validation function returns false
55926 alphanumText : 'This field should only contain letters, numbers and _',
55928 * The keystroke filter mask to be applied on alphanumeric input
55931 alphanumMask : /[a-z0-9_]/i
55933 }();//<script type="text/javascript">
55936 * @class Roo.form.FCKeditor
55937 * @extends Roo.form.TextArea
55938 * Wrapper around the FCKEditor http://www.fckeditor.net
55940 * Creates a new FCKeditor
55941 * @param {Object} config Configuration options
55943 Roo.form.FCKeditor = function(config){
55944 Roo.form.FCKeditor.superclass.constructor.call(this, config);
55947 * @event editorinit
55948 * Fired when the editor is initialized - you can add extra handlers here..
55949 * @param {FCKeditor} this
55950 * @param {Object} the FCK object.
55957 Roo.form.FCKeditor.editors = { };
55958 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
55960 //defaultAutoCreate : {
55961 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
55965 * @cfg {Object} fck options - see fck manual for details.
55970 * @cfg {Object} fck toolbar set (Basic or Default)
55972 toolbarSet : 'Basic',
55974 * @cfg {Object} fck BasePath
55976 basePath : '/fckeditor/',
55984 onRender : function(ct, position)
55987 this.defaultAutoCreate = {
55989 style:"width:300px;height:60px;",
55990 autocomplete: "new-password"
55993 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
55996 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
55997 if(this.preventScrollbars){
55998 this.el.setStyle("overflow", "hidden");
56000 this.el.setHeight(this.growMin);
56003 //console.log('onrender' + this.getId() );
56004 Roo.form.FCKeditor.editors[this.getId()] = this;
56007 this.replaceTextarea() ;
56011 getEditor : function() {
56012 return this.fckEditor;
56015 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
56016 * @param {Mixed} value The value to set
56020 setValue : function(value)
56022 //console.log('setValue: ' + value);
56024 if(typeof(value) == 'undefined') { // not sure why this is happending...
56027 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
56029 //if(!this.el || !this.getEditor()) {
56030 // this.value = value;
56031 //this.setValue.defer(100,this,[value]);
56035 if(!this.getEditor()) {
56039 this.getEditor().SetData(value);
56046 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
56047 * @return {Mixed} value The field value
56049 getValue : function()
56052 if (this.frame && this.frame.dom.style.display == 'none') {
56053 return Roo.form.FCKeditor.superclass.getValue.call(this);
56056 if(!this.el || !this.getEditor()) {
56058 // this.getValue.defer(100,this);
56063 var value=this.getEditor().GetData();
56064 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
56065 return Roo.form.FCKeditor.superclass.getValue.call(this);
56071 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
56072 * @return {Mixed} value The field value
56074 getRawValue : function()
56076 if (this.frame && this.frame.dom.style.display == 'none') {
56077 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
56080 if(!this.el || !this.getEditor()) {
56081 //this.getRawValue.defer(100,this);
56088 var value=this.getEditor().GetData();
56089 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
56090 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
56094 setSize : function(w,h) {
56098 //if (this.frame && this.frame.dom.style.display == 'none') {
56099 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
56102 //if(!this.el || !this.getEditor()) {
56103 // this.setSize.defer(100,this, [w,h]);
56109 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
56111 this.frame.dom.setAttribute('width', w);
56112 this.frame.dom.setAttribute('height', h);
56113 this.frame.setSize(w,h);
56117 toggleSourceEdit : function(value) {
56121 this.el.dom.style.display = value ? '' : 'none';
56122 this.frame.dom.style.display = value ? 'none' : '';
56127 focus: function(tag)
56129 if (this.frame.dom.style.display == 'none') {
56130 return Roo.form.FCKeditor.superclass.focus.call(this);
56132 if(!this.el || !this.getEditor()) {
56133 this.focus.defer(100,this, [tag]);
56140 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
56141 this.getEditor().Focus();
56143 if (!this.getEditor().Selection.GetSelection()) {
56144 this.focus.defer(100,this, [tag]);
56149 var r = this.getEditor().EditorDocument.createRange();
56150 r.setStart(tgs[0],0);
56151 r.setEnd(tgs[0],0);
56152 this.getEditor().Selection.GetSelection().removeAllRanges();
56153 this.getEditor().Selection.GetSelection().addRange(r);
56154 this.getEditor().Focus();
56161 replaceTextarea : function()
56163 if ( document.getElementById( this.getId() + '___Frame' ) ) {
56166 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
56168 // We must check the elements firstly using the Id and then the name.
56169 var oTextarea = document.getElementById( this.getId() );
56171 var colElementsByName = document.getElementsByName( this.getId() ) ;
56173 oTextarea.style.display = 'none' ;
56175 if ( oTextarea.tabIndex ) {
56176 this.TabIndex = oTextarea.tabIndex ;
56179 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
56180 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
56181 this.frame = Roo.get(this.getId() + '___Frame')
56184 _getConfigHtml : function()
56188 for ( var o in this.fckconfig ) {
56189 sConfig += sConfig.length > 0 ? '&' : '';
56190 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
56193 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
56197 _getIFrameHtml : function()
56199 var sFile = 'fckeditor.html' ;
56200 /* no idea what this is about..
56203 if ( (/fcksource=true/i).test( window.top.location.search ) )
56204 sFile = 'fckeditor.original.html' ;
56209 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
56210 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
56213 var html = '<iframe id="' + this.getId() +
56214 '___Frame" src="' + sLink +
56215 '" width="' + this.width +
56216 '" height="' + this.height + '"' +
56217 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
56218 ' frameborder="0" scrolling="no"></iframe>' ;
56223 _insertHtmlBefore : function( html, element )
56225 if ( element.insertAdjacentHTML ) {
56227 element.insertAdjacentHTML( 'beforeBegin', html ) ;
56229 var oRange = document.createRange() ;
56230 oRange.setStartBefore( element ) ;
56231 var oFragment = oRange.createContextualFragment( html );
56232 element.parentNode.insertBefore( oFragment, element ) ;
56245 //Roo.reg('fckeditor', Roo.form.FCKeditor);
56247 function FCKeditor_OnComplete(editorInstance){
56248 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
56249 f.fckEditor = editorInstance;
56250 //console.log("loaded");
56251 f.fireEvent('editorinit', f, editorInstance);
56271 //<script type="text/javascript">
56273 * @class Roo.form.GridField
56274 * @extends Roo.form.Field
56275 * Embed a grid (or editable grid into a form)
56278 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
56280 * xgrid.store = Roo.data.Store
56281 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
56282 * xgrid.store.reader = Roo.data.JsonReader
56286 * Creates a new GridField
56287 * @param {Object} config Configuration options
56289 Roo.form.GridField = function(config){
56290 Roo.form.GridField.superclass.constructor.call(this, config);
56294 Roo.extend(Roo.form.GridField, Roo.form.Field, {
56296 * @cfg {Number} width - used to restrict width of grid..
56300 * @cfg {Number} height - used to restrict height of grid..
56304 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
56310 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56311 * {tag: "input", type: "checkbox", autocomplete: "off"})
56313 // defaultAutoCreate : { tag: 'div' },
56314 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
56316 * @cfg {String} addTitle Text to include for adding a title.
56320 onResize : function(){
56321 Roo.form.Field.superclass.onResize.apply(this, arguments);
56324 initEvents : function(){
56325 // Roo.form.Checkbox.superclass.initEvents.call(this);
56326 // has no events...
56331 getResizeEl : function(){
56335 getPositionEl : function(){
56340 onRender : function(ct, position){
56342 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
56343 var style = this.style;
56346 Roo.form.GridField.superclass.onRender.call(this, ct, position);
56347 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
56348 this.viewEl = this.wrap.createChild({ tag: 'div' });
56350 this.viewEl.applyStyles(style);
56353 this.viewEl.setWidth(this.width);
56356 this.viewEl.setHeight(this.height);
56358 //if(this.inputValue !== undefined){
56359 //this.setValue(this.value);
56362 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
56365 this.grid.render();
56366 this.grid.getDataSource().on('remove', this.refreshValue, this);
56367 this.grid.getDataSource().on('update', this.refreshValue, this);
56368 this.grid.on('afteredit', this.refreshValue, this);
56374 * Sets the value of the item.
56375 * @param {String} either an object or a string..
56377 setValue : function(v){
56379 v = v || []; // empty set..
56380 // this does not seem smart - it really only affects memoryproxy grids..
56381 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
56382 var ds = this.grid.getDataSource();
56383 // assumes a json reader..
56385 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
56386 ds.loadData( data);
56388 // clear selection so it does not get stale.
56389 if (this.grid.sm) {
56390 this.grid.sm.clearSelections();
56393 Roo.form.GridField.superclass.setValue.call(this, v);
56394 this.refreshValue();
56395 // should load data in the grid really....
56399 refreshValue: function() {
56401 this.grid.getDataSource().each(function(r) {
56404 this.el.dom.value = Roo.encode(val);
56412 * Ext JS Library 1.1.1
56413 * Copyright(c) 2006-2007, Ext JS, LLC.
56415 * Originally Released Under LGPL - original licence link has changed is not relivant.
56418 * <script type="text/javascript">
56421 * @class Roo.form.DisplayField
56422 * @extends Roo.form.Field
56423 * A generic Field to display non-editable data.
56424 * @cfg {Boolean} closable (true|false) default false
56426 * Creates a new Display Field item.
56427 * @param {Object} config Configuration options
56429 Roo.form.DisplayField = function(config){
56430 Roo.form.DisplayField.superclass.constructor.call(this, config);
56435 * Fires after the click the close btn
56436 * @param {Roo.form.DisplayField} this
56442 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
56443 inputType: 'hidden',
56449 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56451 focusClass : undefined,
56453 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56455 fieldClass: 'x-form-field',
56458 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
56460 valueRenderer: undefined,
56464 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56465 * {tag: "input", type: "checkbox", autocomplete: "off"})
56468 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
56472 onResize : function(){
56473 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
56477 initEvents : function(){
56478 // Roo.form.Checkbox.superclass.initEvents.call(this);
56479 // has no events...
56482 this.closeEl.on('click', this.onClose, this);
56488 getResizeEl : function(){
56492 getPositionEl : function(){
56497 onRender : function(ct, position){
56499 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
56500 //if(this.inputValue !== undefined){
56501 this.wrap = this.el.wrap();
56503 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
56506 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
56509 if (this.bodyStyle) {
56510 this.viewEl.applyStyles(this.bodyStyle);
56512 //this.viewEl.setStyle('padding', '2px');
56514 this.setValue(this.value);
56519 initValue : Roo.emptyFn,
56524 onClick : function(){
56529 * Sets the checked state of the checkbox.
56530 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
56532 setValue : function(v){
56534 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
56535 // this might be called before we have a dom element..
56536 if (!this.viewEl) {
56539 this.viewEl.dom.innerHTML = html;
56540 Roo.form.DisplayField.superclass.setValue.call(this, v);
56544 onClose : function(e)
56546 e.preventDefault();
56548 this.fireEvent('close', this);
56557 * @class Roo.form.DayPicker
56558 * @extends Roo.form.Field
56559 * A Day picker show [M] [T] [W] ....
56561 * Creates a new Day Picker
56562 * @param {Object} config Configuration options
56564 Roo.form.DayPicker= function(config){
56565 Roo.form.DayPicker.superclass.constructor.call(this, config);
56569 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
56571 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56573 focusClass : undefined,
56575 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56577 fieldClass: "x-form-field",
56580 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56581 * {tag: "input", type: "checkbox", autocomplete: "off"})
56583 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
56586 actionMode : 'viewEl',
56590 inputType : 'hidden',
56593 inputElement: false, // real input element?
56594 basedOn: false, // ????
56596 isFormField: true, // not sure where this is needed!!!!
56598 onResize : function(){
56599 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
56600 if(!this.boxLabel){
56601 this.el.alignTo(this.wrap, 'c-c');
56605 initEvents : function(){
56606 Roo.form.Checkbox.superclass.initEvents.call(this);
56607 this.el.on("click", this.onClick, this);
56608 this.el.on("change", this.onClick, this);
56612 getResizeEl : function(){
56616 getPositionEl : function(){
56622 onRender : function(ct, position){
56623 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
56625 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
56627 var r1 = '<table><tr>';
56628 var r2 = '<tr class="x-form-daypick-icons">';
56629 for (var i=0; i < 7; i++) {
56630 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
56631 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
56634 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
56635 viewEl.select('img').on('click', this.onClick, this);
56636 this.viewEl = viewEl;
56639 // this will not work on Chrome!!!
56640 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
56641 this.el.on('propertychange', this.setFromHidden, this); //ie
56649 initValue : Roo.emptyFn,
56652 * Returns the checked state of the checkbox.
56653 * @return {Boolean} True if checked, else false
56655 getValue : function(){
56656 return this.el.dom.value;
56661 onClick : function(e){
56662 //this.setChecked(!this.checked);
56663 Roo.get(e.target).toggleClass('x-menu-item-checked');
56664 this.refreshValue();
56665 //if(this.el.dom.checked != this.checked){
56666 // this.setValue(this.el.dom.checked);
56671 refreshValue : function()
56674 this.viewEl.select('img',true).each(function(e,i,n) {
56675 val += e.is(".x-menu-item-checked") ? String(n) : '';
56677 this.setValue(val, true);
56681 * Sets the checked state of the checkbox.
56682 * On is always based on a string comparison between inputValue and the param.
56683 * @param {Boolean/String} value - the value to set
56684 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
56686 setValue : function(v,suppressEvent){
56687 if (!this.el.dom) {
56690 var old = this.el.dom.value ;
56691 this.el.dom.value = v;
56692 if (suppressEvent) {
56696 // update display..
56697 this.viewEl.select('img',true).each(function(e,i,n) {
56699 var on = e.is(".x-menu-item-checked");
56700 var newv = v.indexOf(String(n)) > -1;
56702 e.toggleClass('x-menu-item-checked');
56708 this.fireEvent('change', this, v, old);
56713 // handle setting of hidden value by some other method!!?!?
56714 setFromHidden: function()
56719 //console.log("SET FROM HIDDEN");
56720 //alert('setFrom hidden');
56721 this.setValue(this.el.dom.value);
56724 onDestroy : function()
56727 Roo.get(this.viewEl).remove();
56730 Roo.form.DayPicker.superclass.onDestroy.call(this);
56734 * RooJS Library 1.1.1
56735 * Copyright(c) 2008-2011 Alan Knowles
56742 * @class Roo.form.ComboCheck
56743 * @extends Roo.form.ComboBox
56744 * A combobox for multiple select items.
56746 * FIXME - could do with a reset button..
56749 * Create a new ComboCheck
56750 * @param {Object} config Configuration options
56752 Roo.form.ComboCheck = function(config){
56753 Roo.form.ComboCheck.superclass.constructor.call(this, config);
56754 // should verify some data...
56756 // hiddenName = required..
56757 // displayField = required
56758 // valudField == required
56759 var req= [ 'hiddenName', 'displayField', 'valueField' ];
56761 Roo.each(req, function(e) {
56762 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
56763 throw "Roo.form.ComboCheck : missing value for: " + e;
56770 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
56775 selectedClass: 'x-menu-item-checked',
56778 onRender : function(ct, position){
56784 var cls = 'x-combo-list';
56787 this.tpl = new Roo.Template({
56788 html : '<div class="'+cls+'-item x-menu-check-item">' +
56789 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
56790 '<span>{' + this.displayField + '}</span>' +
56797 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
56798 this.view.singleSelect = false;
56799 this.view.multiSelect = true;
56800 this.view.toggleSelect = true;
56801 this.pageTb.add(new Roo.Toolbar.Fill(), {
56804 handler: function()
56811 onViewOver : function(e, t){
56817 onViewClick : function(doFocus,index){
56821 select: function () {
56822 //Roo.log("SELECT CALLED");
56825 selectByValue : function(xv, scrollIntoView){
56826 var ar = this.getValueArray();
56829 Roo.each(ar, function(v) {
56830 if(v === undefined || v === null){
56833 var r = this.findRecord(this.valueField, v);
56835 sels.push(this.store.indexOf(r))
56839 this.view.select(sels);
56845 onSelect : function(record, index){
56846 // Roo.log("onselect Called");
56847 // this is only called by the clear button now..
56848 this.view.clearSelections();
56849 this.setValue('[]');
56850 if (this.value != this.valueBefore) {
56851 this.fireEvent('change', this, this.value, this.valueBefore);
56852 this.valueBefore = this.value;
56855 getValueArray : function()
56860 //Roo.log(this.value);
56861 if (typeof(this.value) == 'undefined') {
56864 var ar = Roo.decode(this.value);
56865 return ar instanceof Array ? ar : []; //?? valid?
56868 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
56873 expand : function ()
56876 Roo.form.ComboCheck.superclass.expand.call(this);
56877 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
56878 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
56883 collapse : function(){
56884 Roo.form.ComboCheck.superclass.collapse.call(this);
56885 var sl = this.view.getSelectedIndexes();
56886 var st = this.store;
56890 Roo.each(sl, function(i) {
56892 nv.push(r.get(this.valueField));
56894 this.setValue(Roo.encode(nv));
56895 if (this.value != this.valueBefore) {
56897 this.fireEvent('change', this, this.value, this.valueBefore);
56898 this.valueBefore = this.value;
56903 setValue : function(v){
56907 var vals = this.getValueArray();
56909 Roo.each(vals, function(k) {
56910 var r = this.findRecord(this.valueField, k);
56912 tv.push(r.data[this.displayField]);
56913 }else if(this.valueNotFoundText !== undefined){
56914 tv.push( this.valueNotFoundText );
56919 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
56920 this.hiddenField.value = v;
56926 * Ext JS Library 1.1.1
56927 * Copyright(c) 2006-2007, Ext JS, LLC.
56929 * Originally Released Under LGPL - original licence link has changed is not relivant.
56932 * <script type="text/javascript">
56936 * @class Roo.form.Signature
56937 * @extends Roo.form.Field
56941 * @param {Object} config Configuration options
56944 Roo.form.Signature = function(config){
56945 Roo.form.Signature.superclass.constructor.call(this, config);
56947 this.addEvents({// not in used??
56950 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
56951 * @param {Roo.form.Signature} combo This combo box
56956 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
56957 * @param {Roo.form.ComboBox} combo This combo box
56958 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
56964 Roo.extend(Roo.form.Signature, Roo.form.Field, {
56966 * @cfg {Object} labels Label to use when rendering a form.
56970 * confirm : "Confirm"
56975 confirm : "Confirm"
56978 * @cfg {Number} width The signature panel width (defaults to 300)
56982 * @cfg {Number} height The signature panel height (defaults to 100)
56986 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
56988 allowBlank : false,
56991 // {Object} signPanel The signature SVG panel element (defaults to {})
56993 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
56994 isMouseDown : false,
56995 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
56996 isConfirmed : false,
56997 // {String} signatureTmp SVG mapping string (defaults to empty string)
57001 defaultAutoCreate : { // modified by initCompnoent..
57007 onRender : function(ct, position){
57009 Roo.form.Signature.superclass.onRender.call(this, ct, position);
57011 this.wrap = this.el.wrap({
57012 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
57015 this.createToolbar(this);
57016 this.signPanel = this.wrap.createChild({
57018 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
57022 this.svgID = Roo.id();
57023 this.svgEl = this.signPanel.createChild({
57024 xmlns : 'http://www.w3.org/2000/svg',
57026 id : this.svgID + "-svg",
57028 height: this.height,
57029 viewBox: '0 0 '+this.width+' '+this.height,
57033 id: this.svgID + "-svg-r",
57035 height: this.height,
57040 id: this.svgID + "-svg-l",
57042 y1: (this.height*0.8), // start set the line in 80% of height
57043 x2: this.width, // end
57044 y2: (this.height*0.8), // end set the line in 80% of height
57046 'stroke-width': "1",
57047 'stroke-dasharray': "3",
57048 'shape-rendering': "crispEdges",
57049 'pointer-events': "none"
57053 id: this.svgID + "-svg-p",
57055 'stroke-width': "3",
57057 'pointer-events': 'none'
57062 this.svgBox = this.svgEl.dom.getScreenCTM();
57064 createSVG : function(){
57065 var svg = this.signPanel;
57066 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
57069 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
57070 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
57071 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
57072 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
57073 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
57074 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
57075 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
57078 isTouchEvent : function(e){
57079 return e.type.match(/^touch/);
57081 getCoords : function (e) {
57082 var pt = this.svgEl.dom.createSVGPoint();
57085 if (this.isTouchEvent(e)) {
57086 pt.x = e.targetTouches[0].clientX;
57087 pt.y = e.targetTouches[0].clientY;
57089 var a = this.svgEl.dom.getScreenCTM();
57090 var b = a.inverse();
57091 var mx = pt.matrixTransform(b);
57092 return mx.x + ',' + mx.y;
57094 //mouse event headler
57095 down : function (e) {
57096 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
57097 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
57099 this.isMouseDown = true;
57101 e.preventDefault();
57103 move : function (e) {
57104 if (this.isMouseDown) {
57105 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
57106 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
57109 e.preventDefault();
57111 up : function (e) {
57112 this.isMouseDown = false;
57113 var sp = this.signatureTmp.split(' ');
57116 if(!sp[sp.length-2].match(/^L/)){
57120 this.signatureTmp = sp.join(" ");
57123 if(this.getValue() != this.signatureTmp){
57124 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57125 this.isConfirmed = false;
57127 e.preventDefault();
57131 * Protected method that will not generally be called directly. It
57132 * is called when the editor creates its toolbar. Override this method if you need to
57133 * add custom toolbar buttons.
57134 * @param {HtmlEditor} editor
57136 createToolbar : function(editor){
57137 function btn(id, toggle, handler){
57138 var xid = fid + '-'+ id ;
57142 cls : 'x-btn-icon x-edit-'+id,
57143 enableToggle:toggle !== false,
57144 scope: editor, // was editor...
57145 handler:handler||editor.relayBtnCmd,
57146 clickEvent:'mousedown',
57147 tooltip: etb.buttonTips[id] || undefined, ///tips ???
57153 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
57157 cls : ' x-signature-btn x-signature-'+id,
57158 scope: editor, // was editor...
57159 handler: this.reset,
57160 clickEvent:'mousedown',
57161 text: this.labels.clear
57168 cls : ' x-signature-btn x-signature-'+id,
57169 scope: editor, // was editor...
57170 handler: this.confirmHandler,
57171 clickEvent:'mousedown',
57172 text: this.labels.confirm
57179 * when user is clicked confirm then show this image.....
57181 * @return {String} Image Data URI
57183 getImageDataURI : function(){
57184 var svg = this.svgEl.dom.parentNode.innerHTML;
57185 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
57190 * @return {Boolean} this.isConfirmed
57192 getConfirmed : function(){
57193 return this.isConfirmed;
57197 * @return {Number} this.width
57199 getWidth : function(){
57204 * @return {Number} this.height
57206 getHeight : function(){
57207 return this.height;
57210 getSignature : function(){
57211 return this.signatureTmp;
57214 reset : function(){
57215 this.signatureTmp = '';
57216 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57217 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
57218 this.isConfirmed = false;
57219 Roo.form.Signature.superclass.reset.call(this);
57221 setSignature : function(s){
57222 this.signatureTmp = s;
57223 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57224 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
57226 this.isConfirmed = false;
57227 Roo.form.Signature.superclass.reset.call(this);
57230 // Roo.log(this.signPanel.dom.contentWindow.up())
57233 setConfirmed : function(){
57237 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
57240 confirmHandler : function(){
57241 if(!this.getSignature()){
57245 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
57246 this.setValue(this.getSignature());
57247 this.isConfirmed = true;
57249 this.fireEvent('confirm', this);
57252 // Subclasses should provide the validation implementation by overriding this
57253 validateValue : function(value){
57254 if(this.allowBlank){
57258 if(this.isConfirmed){
57265 * Ext JS Library 1.1.1
57266 * Copyright(c) 2006-2007, Ext JS, LLC.
57268 * Originally Released Under LGPL - original licence link has changed is not relivant.
57271 * <script type="text/javascript">
57276 * @class Roo.form.ComboBox
57277 * @extends Roo.form.TriggerField
57278 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
57280 * Create a new ComboBox.
57281 * @param {Object} config Configuration options
57283 Roo.form.Select = function(config){
57284 Roo.form.Select.superclass.constructor.call(this, config);
57288 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
57290 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
57293 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
57294 * rendering into an Roo.Editor, defaults to false)
57297 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
57298 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
57301 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
57304 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
57305 * the dropdown list (defaults to undefined, with no header element)
57309 * @cfg {String/Roo.Template} tpl The template to use to render the output
57313 defaultAutoCreate : {tag: "select" },
57315 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
57317 listWidth: undefined,
57319 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
57320 * mode = 'remote' or 'text' if mode = 'local')
57322 displayField: undefined,
57324 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
57325 * mode = 'remote' or 'value' if mode = 'local').
57326 * Note: use of a valueField requires the user make a selection
57327 * in order for a value to be mapped.
57329 valueField: undefined,
57333 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
57334 * field's data value (defaults to the underlying DOM element's name)
57336 hiddenName: undefined,
57338 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
57342 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
57344 selectedClass: 'x-combo-selected',
57346 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
57347 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
57348 * which displays a downward arrow icon).
57350 triggerClass : 'x-form-arrow-trigger',
57352 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
57356 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
57357 * anchor positions (defaults to 'tl-bl')
57359 listAlign: 'tl-bl?',
57361 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
57365 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
57366 * query specified by the allQuery config option (defaults to 'query')
57368 triggerAction: 'query',
57370 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
57371 * (defaults to 4, does not apply if editable = false)
57375 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
57376 * delay (typeAheadDelay) if it matches a known value (defaults to false)
57380 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
57381 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
57385 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
57386 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
57390 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
57391 * when editable = true (defaults to false)
57393 selectOnFocus:false,
57395 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
57397 queryParam: 'query',
57399 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
57400 * when mode = 'remote' (defaults to 'Loading...')
57402 loadingText: 'Loading...',
57404 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
57408 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
57412 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
57413 * traditional select (defaults to true)
57417 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
57421 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
57425 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
57426 * listWidth has a higher value)
57430 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
57431 * allow the user to set arbitrary text into the field (defaults to false)
57433 forceSelection:false,
57435 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
57436 * if typeAhead = true (defaults to 250)
57438 typeAheadDelay : 250,
57440 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
57441 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
57443 valueNotFoundText : undefined,
57446 * @cfg {String} defaultValue The value displayed after loading the store.
57451 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
57453 blockFocus : false,
57456 * @cfg {Boolean} disableClear Disable showing of clear button.
57458 disableClear : false,
57460 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
57462 alwaysQuery : false,
57468 // element that contains real text value.. (when hidden is used..)
57471 onRender : function(ct, position){
57472 Roo.form.Field.prototype.onRender.call(this, ct, position);
57475 this.store.on('beforeload', this.onBeforeLoad, this);
57476 this.store.on('load', this.onLoad, this);
57477 this.store.on('loadexception', this.onLoadException, this);
57478 this.store.load({});
57486 initEvents : function(){
57487 //Roo.form.ComboBox.superclass.initEvents.call(this);
57491 onDestroy : function(){
57494 this.store.un('beforeload', this.onBeforeLoad, this);
57495 this.store.un('load', this.onLoad, this);
57496 this.store.un('loadexception', this.onLoadException, this);
57498 //Roo.form.ComboBox.superclass.onDestroy.call(this);
57502 fireKey : function(e){
57503 if(e.isNavKeyPress() && !this.list.isVisible()){
57504 this.fireEvent("specialkey", this, e);
57509 onResize: function(w, h){
57517 * Allow or prevent the user from directly editing the field text. If false is passed,
57518 * the user will only be able to select from the items defined in the dropdown list. This method
57519 * is the runtime equivalent of setting the 'editable' config option at config time.
57520 * @param {Boolean} value True to allow the user to directly edit the field text
57522 setEditable : function(value){
57527 onBeforeLoad : function(){
57529 Roo.log("Select before load");
57532 this.innerList.update(this.loadingText ?
57533 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
57534 //this.restrictHeight();
57535 this.selectedIndex = -1;
57539 onLoad : function(){
57542 var dom = this.el.dom;
57543 dom.innerHTML = '';
57544 var od = dom.ownerDocument;
57546 if (this.emptyText) {
57547 var op = od.createElement('option');
57548 op.setAttribute('value', '');
57549 op.innerHTML = String.format('{0}', this.emptyText);
57550 dom.appendChild(op);
57552 if(this.store.getCount() > 0){
57554 var vf = this.valueField;
57555 var df = this.displayField;
57556 this.store.data.each(function(r) {
57557 // which colmsn to use... testing - cdoe / title..
57558 var op = od.createElement('option');
57559 op.setAttribute('value', r.data[vf]);
57560 op.innerHTML = String.format('{0}', r.data[df]);
57561 dom.appendChild(op);
57563 if (typeof(this.defaultValue != 'undefined')) {
57564 this.setValue(this.defaultValue);
57569 //this.onEmptyResults();
57574 onLoadException : function()
57576 dom.innerHTML = '';
57578 Roo.log("Select on load exception");
57582 Roo.log(this.store.reader.jsonData);
57583 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57584 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57590 onTypeAhead : function(){
57595 onSelect : function(record, index){
57596 Roo.log('on select?');
57598 if(this.fireEvent('beforeselect', this, record, index) !== false){
57599 this.setFromData(index > -1 ? record.data : false);
57601 this.fireEvent('select', this, record, index);
57606 * Returns the currently selected field value or empty string if no value is set.
57607 * @return {String} value The selected value
57609 getValue : function(){
57610 var dom = this.el.dom;
57611 this.value = dom.options[dom.selectedIndex].value;
57617 * Clears any text/value currently set in the field
57619 clearValue : function(){
57621 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
57626 * Sets the specified value into the field. If the value finds a match, the corresponding record text
57627 * will be displayed in the field. If the value does not match the data value of an existing item,
57628 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
57629 * Otherwise the field will be blank (although the value will still be set).
57630 * @param {String} value The value to match
57632 setValue : function(v){
57633 var d = this.el.dom;
57634 for (var i =0; i < d.options.length;i++) {
57635 if (v == d.options[i].value) {
57636 d.selectedIndex = i;
57644 * @property {Object} the last set data for the element
57649 * Sets the value of the field based on a object which is related to the record format for the store.
57650 * @param {Object} value the value to set as. or false on reset?
57652 setFromData : function(o){
57653 Roo.log('setfrom data?');
57659 reset : function(){
57663 findRecord : function(prop, value){
57668 if(this.store.getCount() > 0){
57669 this.store.each(function(r){
57670 if(r.data[prop] == value){
57680 getName: function()
57682 // returns hidden if it's set..
57683 if (!this.rendered) {return ''};
57684 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
57692 onEmptyResults : function(){
57693 Roo.log('empty results');
57698 * Returns true if the dropdown list is expanded, else false.
57700 isExpanded : function(){
57705 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
57706 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57707 * @param {String} value The data value of the item to select
57708 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57709 * selected item if it is not currently in view (defaults to true)
57710 * @return {Boolean} True if the value matched an item in the list, else false
57712 selectByValue : function(v, scrollIntoView){
57713 Roo.log('select By Value');
57716 if(v !== undefined && v !== null){
57717 var r = this.findRecord(this.valueField || this.displayField, v);
57719 this.select(this.store.indexOf(r), scrollIntoView);
57727 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
57728 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57729 * @param {Number} index The zero-based index of the list item to select
57730 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57731 * selected item if it is not currently in view (defaults to true)
57733 select : function(index, scrollIntoView){
57734 Roo.log('select ');
57737 this.selectedIndex = index;
57738 this.view.select(index);
57739 if(scrollIntoView !== false){
57740 var el = this.view.getNode(index);
57742 this.innerList.scrollChildIntoView(el, false);
57750 validateBlur : function(){
57757 initQuery : function(){
57758 this.doQuery(this.getRawValue());
57762 doForce : function(){
57763 if(this.el.dom.value.length > 0){
57764 this.el.dom.value =
57765 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
57771 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
57772 * query allowing the query action to be canceled if needed.
57773 * @param {String} query The SQL query to execute
57774 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
57775 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
57776 * saved in the current store (defaults to false)
57778 doQuery : function(q, forceAll){
57780 Roo.log('doQuery?');
57781 if(q === undefined || q === null){
57786 forceAll: forceAll,
57790 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
57794 forceAll = qe.forceAll;
57795 if(forceAll === true || (q.length >= this.minChars)){
57796 if(this.lastQuery != q || this.alwaysQuery){
57797 this.lastQuery = q;
57798 if(this.mode == 'local'){
57799 this.selectedIndex = -1;
57801 this.store.clearFilter();
57803 this.store.filter(this.displayField, q);
57807 this.store.baseParams[this.queryParam] = q;
57809 params: this.getParams(q)
57814 this.selectedIndex = -1;
57821 getParams : function(q){
57823 //p[this.queryParam] = q;
57826 p.limit = this.pageSize;
57832 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
57834 collapse : function(){
57839 collapseIf : function(e){
57844 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
57846 expand : function(){
57854 * @cfg {Boolean} grow
57858 * @cfg {Number} growMin
57862 * @cfg {Number} growMax
57870 setWidth : function()
57874 getResizeEl : function(){
57877 });//<script type="text/javasscript">
57881 * @class Roo.DDView
57882 * A DnD enabled version of Roo.View.
57883 * @param {Element/String} container The Element in which to create the View.
57884 * @param {String} tpl The template string used to create the markup for each element of the View
57885 * @param {Object} config The configuration properties. These include all the config options of
57886 * {@link Roo.View} plus some specific to this class.<br>
57888 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
57889 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
57891 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
57892 .x-view-drag-insert-above {
57893 border-top:1px dotted #3366cc;
57895 .x-view-drag-insert-below {
57896 border-bottom:1px dotted #3366cc;
57902 Roo.DDView = function(container, tpl, config) {
57903 Roo.DDView.superclass.constructor.apply(this, arguments);
57904 this.getEl().setStyle("outline", "0px none");
57905 this.getEl().unselectable();
57906 if (this.dragGroup) {
57907 this.setDraggable(this.dragGroup.split(","));
57909 if (this.dropGroup) {
57910 this.setDroppable(this.dropGroup.split(","));
57912 if (this.deletable) {
57913 this.setDeletable();
57915 this.isDirtyFlag = false;
57921 Roo.extend(Roo.DDView, Roo.View, {
57922 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
57923 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
57924 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
57925 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
57929 reset: Roo.emptyFn,
57931 clearInvalid: Roo.form.Field.prototype.clearInvalid,
57933 validate: function() {
57937 destroy: function() {
57938 this.purgeListeners();
57939 this.getEl.removeAllListeners();
57940 this.getEl().remove();
57941 if (this.dragZone) {
57942 if (this.dragZone.destroy) {
57943 this.dragZone.destroy();
57946 if (this.dropZone) {
57947 if (this.dropZone.destroy) {
57948 this.dropZone.destroy();
57953 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
57954 getName: function() {
57958 /** Loads the View from a JSON string representing the Records to put into the Store. */
57959 setValue: function(v) {
57961 throw "DDView.setValue(). DDView must be constructed with a valid Store";
57964 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
57965 this.store.proxy = new Roo.data.MemoryProxy(data);
57969 /** @return {String} a parenthesised list of the ids of the Records in the View. */
57970 getValue: function() {
57972 this.store.each(function(rec) {
57973 result += rec.id + ',';
57975 return result.substr(0, result.length - 1) + ')';
57978 getIds: function() {
57979 var i = 0, result = new Array(this.store.getCount());
57980 this.store.each(function(rec) {
57981 result[i++] = rec.id;
57986 isDirty: function() {
57987 return this.isDirtyFlag;
57991 * Part of the Roo.dd.DropZone interface. If no target node is found, the
57992 * whole Element becomes the target, and this causes the drop gesture to append.
57994 getTargetFromEvent : function(e) {
57995 var target = e.getTarget();
57996 while ((target !== null) && (target.parentNode != this.el.dom)) {
57997 target = target.parentNode;
58000 target = this.el.dom.lastChild || this.el.dom;
58006 * Create the drag data which consists of an object which has the property "ddel" as
58007 * the drag proxy element.
58009 getDragData : function(e) {
58010 var target = this.findItemFromChild(e.getTarget());
58012 this.handleSelection(e);
58013 var selNodes = this.getSelectedNodes();
58016 copy: this.copy || (this.allowCopy && e.ctrlKey),
58020 var selectedIndices = this.getSelectedIndexes();
58021 for (var i = 0; i < selectedIndices.length; i++) {
58022 dragData.records.push(this.store.getAt(selectedIndices[i]));
58024 if (selNodes.length == 1) {
58025 dragData.ddel = target.cloneNode(true); // the div element
58027 var div = document.createElement('div'); // create the multi element drag "ghost"
58028 div.className = 'multi-proxy';
58029 for (var i = 0, len = selNodes.length; i < len; i++) {
58030 div.appendChild(selNodes[i].cloneNode(true));
58032 dragData.ddel = div;
58034 //console.log(dragData)
58035 //console.log(dragData.ddel.innerHTML)
58038 //console.log('nodragData')
58042 /** Specify to which ddGroup items in this DDView may be dragged. */
58043 setDraggable: function(ddGroup) {
58044 if (ddGroup instanceof Array) {
58045 Roo.each(ddGroup, this.setDraggable, this);
58048 if (this.dragZone) {
58049 this.dragZone.addToGroup(ddGroup);
58051 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
58052 containerScroll: true,
58056 // Draggability implies selection. DragZone's mousedown selects the element.
58057 if (!this.multiSelect) { this.singleSelect = true; }
58059 // Wire the DragZone's handlers up to methods in *this*
58060 this.dragZone.getDragData = this.getDragData.createDelegate(this);
58064 /** Specify from which ddGroup this DDView accepts drops. */
58065 setDroppable: function(ddGroup) {
58066 if (ddGroup instanceof Array) {
58067 Roo.each(ddGroup, this.setDroppable, this);
58070 if (this.dropZone) {
58071 this.dropZone.addToGroup(ddGroup);
58073 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
58074 containerScroll: true,
58078 // Wire the DropZone's handlers up to methods in *this*
58079 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
58080 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
58081 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
58082 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
58083 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
58087 /** Decide whether to drop above or below a View node. */
58088 getDropPoint : function(e, n, dd){
58089 if (n == this.el.dom) { return "above"; }
58090 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
58091 var c = t + (b - t) / 2;
58092 var y = Roo.lib.Event.getPageY(e);
58100 onNodeEnter : function(n, dd, e, data){
58104 onNodeOver : function(n, dd, e, data){
58105 var pt = this.getDropPoint(e, n, dd);
58106 // set the insert point style on the target node
58107 var dragElClass = this.dropNotAllowed;
58110 if (pt == "above"){
58111 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
58112 targetElClass = "x-view-drag-insert-above";
58114 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
58115 targetElClass = "x-view-drag-insert-below";
58117 if (this.lastInsertClass != targetElClass){
58118 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
58119 this.lastInsertClass = targetElClass;
58122 return dragElClass;
58125 onNodeOut : function(n, dd, e, data){
58126 this.removeDropIndicators(n);
58129 onNodeDrop : function(n, dd, e, data){
58130 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
58133 var pt = this.getDropPoint(e, n, dd);
58134 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
58135 if (pt == "below") { insertAt++; }
58136 for (var i = 0; i < data.records.length; i++) {
58137 var r = data.records[i];
58138 var dup = this.store.getById(r.id);
58139 if (dup && (dd != this.dragZone)) {
58140 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
58143 this.store.insert(insertAt++, r.copy());
58145 data.source.isDirtyFlag = true;
58147 this.store.insert(insertAt++, r);
58149 this.isDirtyFlag = true;
58152 this.dragZone.cachedTarget = null;
58156 removeDropIndicators : function(n){
58158 Roo.fly(n).removeClass([
58159 "x-view-drag-insert-above",
58160 "x-view-drag-insert-below"]);
58161 this.lastInsertClass = "_noclass";
58166 * Utility method. Add a delete option to the DDView's context menu.
58167 * @param {String} imageUrl The URL of the "delete" icon image.
58169 setDeletable: function(imageUrl) {
58170 if (!this.singleSelect && !this.multiSelect) {
58171 this.singleSelect = true;
58173 var c = this.getContextMenu();
58174 this.contextMenu.on("itemclick", function(item) {
58177 this.remove(this.getSelectedIndexes());
58181 this.contextMenu.add({
58188 /** Return the context menu for this DDView. */
58189 getContextMenu: function() {
58190 if (!this.contextMenu) {
58191 // Create the View's context menu
58192 this.contextMenu = new Roo.menu.Menu({
58193 id: this.id + "-contextmenu"
58195 this.el.on("contextmenu", this.showContextMenu, this);
58197 return this.contextMenu;
58200 disableContextMenu: function() {
58201 if (this.contextMenu) {
58202 this.el.un("contextmenu", this.showContextMenu, this);
58206 showContextMenu: function(e, item) {
58207 item = this.findItemFromChild(e.getTarget());
58210 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
58211 this.contextMenu.showAt(e.getXY());
58216 * Remove {@link Roo.data.Record}s at the specified indices.
58217 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
58219 remove: function(selectedIndices) {
58220 selectedIndices = [].concat(selectedIndices);
58221 for (var i = 0; i < selectedIndices.length; i++) {
58222 var rec = this.store.getAt(selectedIndices[i]);
58223 this.store.remove(rec);
58228 * Double click fires the event, but also, if this is draggable, and there is only one other
58229 * related DropZone, it transfers the selected node.
58231 onDblClick : function(e){
58232 var item = this.findItemFromChild(e.getTarget());
58234 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
58237 if (this.dragGroup) {
58238 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
58239 while (targets.indexOf(this.dropZone) > -1) {
58240 targets.remove(this.dropZone);
58242 if (targets.length == 1) {
58243 this.dragZone.cachedTarget = null;
58244 var el = Roo.get(targets[0].getEl());
58245 var box = el.getBox(true);
58246 targets[0].onNodeDrop(el.dom, {
58248 xy: [box.x, box.y + box.height - 1]
58249 }, null, this.getDragData(e));
58255 handleSelection: function(e) {
58256 this.dragZone.cachedTarget = null;
58257 var item = this.findItemFromChild(e.getTarget());
58259 this.clearSelections(true);
58262 if (item && (this.multiSelect || this.singleSelect)){
58263 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
58264 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
58265 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
58266 this.unselect(item);
58268 this.select(item, this.multiSelect && e.ctrlKey);
58269 this.lastSelection = item;
58274 onItemClick : function(item, index, e){
58275 if(this.fireEvent("beforeclick", this, index, item, e) === false){
58281 unselect : function(nodeInfo, suppressEvent){
58282 var node = this.getNode(nodeInfo);
58283 if(node && this.isSelected(node)){
58284 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
58285 Roo.fly(node).removeClass(this.selectedClass);
58286 this.selections.remove(node);
58287 if(!suppressEvent){
58288 this.fireEvent("selectionchange", this, this.selections);
58296 * Ext JS Library 1.1.1
58297 * Copyright(c) 2006-2007, Ext JS, LLC.
58299 * Originally Released Under LGPL - original licence link has changed is not relivant.
58302 * <script type="text/javascript">
58306 * @class Roo.LayoutManager
58307 * @extends Roo.util.Observable
58308 * Base class for layout managers.
58310 Roo.LayoutManager = function(container, config){
58311 Roo.LayoutManager.superclass.constructor.call(this);
58312 this.el = Roo.get(container);
58313 // ie scrollbar fix
58314 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
58315 document.body.scroll = "no";
58316 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
58317 this.el.position('relative');
58319 this.id = this.el.id;
58320 this.el.addClass("x-layout-container");
58321 /** false to disable window resize monitoring @type Boolean */
58322 this.monitorWindowResize = true;
58327 * Fires when a layout is performed.
58328 * @param {Roo.LayoutManager} this
58332 * @event regionresized
58333 * Fires when the user resizes a region.
58334 * @param {Roo.LayoutRegion} region The resized region
58335 * @param {Number} newSize The new size (width for east/west, height for north/south)
58337 "regionresized" : true,
58339 * @event regioncollapsed
58340 * Fires when a region is collapsed.
58341 * @param {Roo.LayoutRegion} region The collapsed region
58343 "regioncollapsed" : true,
58345 * @event regionexpanded
58346 * Fires when a region is expanded.
58347 * @param {Roo.LayoutRegion} region The expanded region
58349 "regionexpanded" : true
58351 this.updating = false;
58352 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58355 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
58357 * Returns true if this layout is currently being updated
58358 * @return {Boolean}
58360 isUpdating : function(){
58361 return this.updating;
58365 * Suspend the LayoutManager from doing auto-layouts while
58366 * making multiple add or remove calls
58368 beginUpdate : function(){
58369 this.updating = true;
58373 * Restore auto-layouts and optionally disable the manager from performing a layout
58374 * @param {Boolean} noLayout true to disable a layout update
58376 endUpdate : function(noLayout){
58377 this.updating = false;
58383 layout: function(){
58387 onRegionResized : function(region, newSize){
58388 this.fireEvent("regionresized", region, newSize);
58392 onRegionCollapsed : function(region){
58393 this.fireEvent("regioncollapsed", region);
58396 onRegionExpanded : function(region){
58397 this.fireEvent("regionexpanded", region);
58401 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
58402 * performs box-model adjustments.
58403 * @return {Object} The size as an object {width: (the width), height: (the height)}
58405 getViewSize : function(){
58407 if(this.el.dom != document.body){
58408 size = this.el.getSize();
58410 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
58412 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
58413 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
58418 * Returns the Element this layout is bound to.
58419 * @return {Roo.Element}
58421 getEl : function(){
58426 * Returns the specified region.
58427 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
58428 * @return {Roo.LayoutRegion}
58430 getRegion : function(target){
58431 return this.regions[target.toLowerCase()];
58434 onWindowResize : function(){
58435 if(this.monitorWindowResize){
58441 * Ext JS Library 1.1.1
58442 * Copyright(c) 2006-2007, Ext JS, LLC.
58444 * Originally Released Under LGPL - original licence link has changed is not relivant.
58447 * <script type="text/javascript">
58450 * @class Roo.BorderLayout
58451 * @extends Roo.LayoutManager
58452 * @children Roo.ContentPanel
58453 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
58454 * please see: <br><br>
58455 * <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>
58456 * <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>
58459 var layout = new Roo.BorderLayout(document.body, {
58493 preferredTabWidth: 150
58498 var CP = Roo.ContentPanel;
58500 layout.beginUpdate();
58501 layout.add("north", new CP("north", "North"));
58502 layout.add("south", new CP("south", {title: "South", closable: true}));
58503 layout.add("west", new CP("west", {title: "West"}));
58504 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
58505 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
58506 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
58507 layout.getRegion("center").showPanel("center1");
58508 layout.endUpdate();
58511 <b>The container the layout is rendered into can be either the body element or any other element.
58512 If it is not the body element, the container needs to either be an absolute positioned element,
58513 or you will need to add "position:relative" to the css of the container. You will also need to specify
58514 the container size if it is not the body element.</b>
58517 * Create a new BorderLayout
58518 * @param {String/HTMLElement/Element} container The container this layout is bound to
58519 * @param {Object} config Configuration options
58521 Roo.BorderLayout = function(container, config){
58522 config = config || {};
58523 Roo.BorderLayout.superclass.constructor.call(this, container, config);
58524 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
58525 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
58526 var target = this.factory.validRegions[i];
58527 if(config[target]){
58528 this.addRegion(target, config[target]);
58533 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
58536 * @cfg {Roo.LayoutRegion} east
58539 * @cfg {Roo.LayoutRegion} west
58542 * @cfg {Roo.LayoutRegion} north
58545 * @cfg {Roo.LayoutRegion} south
58548 * @cfg {Roo.LayoutRegion} center
58551 * Creates and adds a new region if it doesn't already exist.
58552 * @param {String} target The target region key (north, south, east, west or center).
58553 * @param {Object} config The regions config object
58554 * @return {BorderLayoutRegion} The new region
58556 addRegion : function(target, config){
58557 if(!this.regions[target]){
58558 var r = this.factory.create(target, this, config);
58559 this.bindRegion(target, r);
58561 return this.regions[target];
58565 bindRegion : function(name, r){
58566 this.regions[name] = r;
58567 r.on("visibilitychange", this.layout, this);
58568 r.on("paneladded", this.layout, this);
58569 r.on("panelremoved", this.layout, this);
58570 r.on("invalidated", this.layout, this);
58571 r.on("resized", this.onRegionResized, this);
58572 r.on("collapsed", this.onRegionCollapsed, this);
58573 r.on("expanded", this.onRegionExpanded, this);
58577 * Performs a layout update.
58579 layout : function(){
58580 if(this.updating) {
58583 var size = this.getViewSize();
58584 var w = size.width;
58585 var h = size.height;
58590 //var x = 0, y = 0;
58592 var rs = this.regions;
58593 var north = rs["north"];
58594 var south = rs["south"];
58595 var west = rs["west"];
58596 var east = rs["east"];
58597 var center = rs["center"];
58598 //if(this.hideOnLayout){ // not supported anymore
58599 //c.el.setStyle("display", "none");
58601 if(north && north.isVisible()){
58602 var b = north.getBox();
58603 var m = north.getMargins();
58604 b.width = w - (m.left+m.right);
58607 centerY = b.height + b.y + m.bottom;
58608 centerH -= centerY;
58609 north.updateBox(this.safeBox(b));
58611 if(south && south.isVisible()){
58612 var b = south.getBox();
58613 var m = south.getMargins();
58614 b.width = w - (m.left+m.right);
58616 var totalHeight = (b.height + m.top + m.bottom);
58617 b.y = h - totalHeight + m.top;
58618 centerH -= totalHeight;
58619 south.updateBox(this.safeBox(b));
58621 if(west && west.isVisible()){
58622 var b = west.getBox();
58623 var m = west.getMargins();
58624 b.height = centerH - (m.top+m.bottom);
58626 b.y = centerY + m.top;
58627 var totalWidth = (b.width + m.left + m.right);
58628 centerX += totalWidth;
58629 centerW -= totalWidth;
58630 west.updateBox(this.safeBox(b));
58632 if(east && east.isVisible()){
58633 var b = east.getBox();
58634 var m = east.getMargins();
58635 b.height = centerH - (m.top+m.bottom);
58636 var totalWidth = (b.width + m.left + m.right);
58637 b.x = w - totalWidth + m.left;
58638 b.y = centerY + m.top;
58639 centerW -= totalWidth;
58640 east.updateBox(this.safeBox(b));
58643 var m = center.getMargins();
58645 x: centerX + m.left,
58646 y: centerY + m.top,
58647 width: centerW - (m.left+m.right),
58648 height: centerH - (m.top+m.bottom)
58650 //if(this.hideOnLayout){
58651 //center.el.setStyle("display", "block");
58653 center.updateBox(this.safeBox(centerBox));
58656 this.fireEvent("layout", this);
58660 safeBox : function(box){
58661 box.width = Math.max(0, box.width);
58662 box.height = Math.max(0, box.height);
58667 * Adds a ContentPanel (or subclass) to this layout.
58668 * @param {String} target The target region key (north, south, east, west or center).
58669 * @param {Roo.ContentPanel} panel The panel to add
58670 * @return {Roo.ContentPanel} The added panel
58672 add : function(target, panel){
58674 target = target.toLowerCase();
58675 return this.regions[target].add(panel);
58679 * Remove a ContentPanel (or subclass) to this layout.
58680 * @param {String} target The target region key (north, south, east, west or center).
58681 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
58682 * @return {Roo.ContentPanel} The removed panel
58684 remove : function(target, panel){
58685 target = target.toLowerCase();
58686 return this.regions[target].remove(panel);
58690 * Searches all regions for a panel with the specified id
58691 * @param {String} panelId
58692 * @return {Roo.ContentPanel} The panel or null if it wasn't found
58694 findPanel : function(panelId){
58695 var rs = this.regions;
58696 for(var target in rs){
58697 if(typeof rs[target] != "function"){
58698 var p = rs[target].getPanel(panelId);
58708 * Searches all regions for a panel with the specified id and activates (shows) it.
58709 * @param {String/ContentPanel} panelId The panels id or the panel itself
58710 * @return {Roo.ContentPanel} The shown panel or null
58712 showPanel : function(panelId) {
58713 var rs = this.regions;
58714 for(var target in rs){
58715 var r = rs[target];
58716 if(typeof r != "function"){
58717 if(r.hasPanel(panelId)){
58718 return r.showPanel(panelId);
58726 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
58727 * @param {Roo.state.Provider} provider (optional) An alternate state provider
58729 restoreState : function(provider){
58731 provider = Roo.state.Manager;
58733 var sm = new Roo.LayoutStateManager();
58734 sm.init(this, provider);
58738 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
58739 * object should contain properties for each region to add ContentPanels to, and each property's value should be
58740 * a valid ContentPanel config object. Example:
58742 // Create the main layout
58743 var layout = new Roo.BorderLayout('main-ct', {
58754 // Create and add multiple ContentPanels at once via configs
58757 id: 'source-files',
58759 title:'Ext Source Files',
58772 * @param {Object} regions An object containing ContentPanel configs by region name
58774 batchAdd : function(regions){
58775 this.beginUpdate();
58776 for(var rname in regions){
58777 var lr = this.regions[rname];
58779 this.addTypedPanels(lr, regions[rname]);
58786 addTypedPanels : function(lr, ps){
58787 if(typeof ps == 'string'){
58788 lr.add(new Roo.ContentPanel(ps));
58790 else if(ps instanceof Array){
58791 for(var i =0, len = ps.length; i < len; i++){
58792 this.addTypedPanels(lr, ps[i]);
58795 else if(!ps.events){ // raw config?
58797 delete ps.el; // prevent conflict
58798 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
58800 else { // panel object assumed!
58805 * Adds a xtype elements to the layout.
58809 xtype : 'ContentPanel',
58816 xtype : 'NestedLayoutPanel',
58822 items : [ ... list of content panels or nested layout panels.. ]
58826 * @param {Object} cfg Xtype definition of item to add.
58828 addxtype : function(cfg)
58830 // basically accepts a pannel...
58831 // can accept a layout region..!?!?
58832 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
58834 if (!cfg.xtype.match(/Panel$/)) {
58839 if (typeof(cfg.region) == 'undefined') {
58840 Roo.log("Failed to add Panel, region was not set");
58844 var region = cfg.region;
58850 xitems = cfg.items;
58857 case 'ContentPanel': // ContentPanel (el, cfg)
58858 case 'ScrollPanel': // ContentPanel (el, cfg)
58860 if(cfg.autoCreate) {
58861 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58863 var el = this.el.createChild();
58864 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
58867 this.add(region, ret);
58871 case 'TreePanel': // our new panel!
58872 cfg.el = this.el.createChild();
58873 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58874 this.add(region, ret);
58877 case 'NestedLayoutPanel':
58878 // create a new Layout (which is a Border Layout...
58879 var el = this.el.createChild();
58880 var clayout = cfg.layout;
58882 clayout.items = clayout.items || [];
58883 // replace this exitems with the clayout ones..
58884 xitems = clayout.items;
58887 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
58888 cfg.background = false;
58890 var layout = new Roo.BorderLayout(el, clayout);
58892 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
58893 //console.log('adding nested layout panel ' + cfg.toSource());
58894 this.add(region, ret);
58895 nb = {}; /// find first...
58900 // needs grid and region
58902 //var el = this.getRegion(region).el.createChild();
58903 var el = this.el.createChild();
58904 // create the grid first...
58906 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
58908 if (region == 'center' && this.active ) {
58909 cfg.background = false;
58911 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
58913 this.add(region, ret);
58914 if (cfg.background) {
58915 ret.on('activate', function(gp) {
58916 if (!gp.grid.rendered) {
58931 if (typeof(Roo[cfg.xtype]) != 'undefined') {
58933 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58934 this.add(region, ret);
58937 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
58941 // GridPanel (grid, cfg)
58944 this.beginUpdate();
58948 Roo.each(xitems, function(i) {
58949 region = nb && i.region ? i.region : false;
58951 var add = ret.addxtype(i);
58954 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
58955 if (!i.background) {
58956 abn[region] = nb[region] ;
58963 // make the last non-background panel active..
58964 //if (nb) { Roo.log(abn); }
58967 for(var r in abn) {
58968 region = this.getRegion(r);
58970 // tried using nb[r], but it does not work..
58972 region.showPanel(abn[r]);
58983 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
58984 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
58985 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
58986 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
58989 var CP = Roo.ContentPanel;
58991 var layout = Roo.BorderLayout.create({
58995 panels: [new CP("north", "North")]
59004 panels: [new CP("west", {title: "West"})]
59013 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
59022 panels: [new CP("south", {title: "South", closable: true})]
59029 preferredTabWidth: 150,
59031 new CP("center1", {title: "Close Me", closable: true}),
59032 new CP("center2", {title: "Center Panel", closable: false})
59037 layout.getRegion("center").showPanel("center1");
59042 Roo.BorderLayout.create = function(config, targetEl){
59043 var layout = new Roo.BorderLayout(targetEl || document.body, config);
59044 layout.beginUpdate();
59045 var regions = Roo.BorderLayout.RegionFactory.validRegions;
59046 for(var j = 0, jlen = regions.length; j < jlen; j++){
59047 var lr = regions[j];
59048 if(layout.regions[lr] && config[lr].panels){
59049 var r = layout.regions[lr];
59050 var ps = config[lr].panels;
59051 layout.addTypedPanels(r, ps);
59054 layout.endUpdate();
59059 Roo.BorderLayout.RegionFactory = {
59061 validRegions : ["north","south","east","west","center"],
59064 create : function(target, mgr, config){
59065 target = target.toLowerCase();
59066 if(config.lightweight || config.basic){
59067 return new Roo.BasicLayoutRegion(mgr, config, target);
59071 return new Roo.NorthLayoutRegion(mgr, config);
59073 return new Roo.SouthLayoutRegion(mgr, config);
59075 return new Roo.EastLayoutRegion(mgr, config);
59077 return new Roo.WestLayoutRegion(mgr, config);
59079 return new Roo.CenterLayoutRegion(mgr, config);
59081 throw 'Layout region "'+target+'" not supported.';
59085 * Ext JS Library 1.1.1
59086 * Copyright(c) 2006-2007, Ext JS, LLC.
59088 * Originally Released Under LGPL - original licence link has changed is not relivant.
59091 * <script type="text/javascript">
59095 * @class Roo.BasicLayoutRegion
59096 * @extends Roo.util.Observable
59097 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
59098 * and does not have a titlebar, tabs or any other features. All it does is size and position
59099 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
59101 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
59103 this.position = pos;
59106 * @scope Roo.BasicLayoutRegion
59110 * @event beforeremove
59111 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
59112 * @param {Roo.LayoutRegion} this
59113 * @param {Roo.ContentPanel} panel The panel
59114 * @param {Object} e The cancel event object
59116 "beforeremove" : true,
59118 * @event invalidated
59119 * Fires when the layout for this region is changed.
59120 * @param {Roo.LayoutRegion} this
59122 "invalidated" : true,
59124 * @event visibilitychange
59125 * Fires when this region is shown or hidden
59126 * @param {Roo.LayoutRegion} this
59127 * @param {Boolean} visibility true or false
59129 "visibilitychange" : true,
59131 * @event paneladded
59132 * Fires when a panel is added.
59133 * @param {Roo.LayoutRegion} this
59134 * @param {Roo.ContentPanel} panel The panel
59136 "paneladded" : true,
59138 * @event panelremoved
59139 * Fires when a panel is removed.
59140 * @param {Roo.LayoutRegion} this
59141 * @param {Roo.ContentPanel} panel The panel
59143 "panelremoved" : true,
59145 * @event beforecollapse
59146 * Fires when this region before collapse.
59147 * @param {Roo.LayoutRegion} this
59149 "beforecollapse" : true,
59152 * Fires when this region is collapsed.
59153 * @param {Roo.LayoutRegion} this
59155 "collapsed" : true,
59158 * Fires when this region is expanded.
59159 * @param {Roo.LayoutRegion} this
59164 * Fires when this region is slid into view.
59165 * @param {Roo.LayoutRegion} this
59167 "slideshow" : true,
59170 * Fires when this region slides out of view.
59171 * @param {Roo.LayoutRegion} this
59173 "slidehide" : true,
59175 * @event panelactivated
59176 * Fires when a panel is activated.
59177 * @param {Roo.LayoutRegion} this
59178 * @param {Roo.ContentPanel} panel The activated panel
59180 "panelactivated" : true,
59183 * Fires when the user resizes this region.
59184 * @param {Roo.LayoutRegion} this
59185 * @param {Number} newSize The new size (width for east/west, height for north/south)
59189 /** A collection of panels in this region. @type Roo.util.MixedCollection */
59190 this.panels = new Roo.util.MixedCollection();
59191 this.panels.getKey = this.getPanelId.createDelegate(this);
59193 this.activePanel = null;
59194 // ensure listeners are added...
59196 if (config.listeners || config.events) {
59197 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
59198 listeners : config.listeners || {},
59199 events : config.events || {}
59203 if(skipConfig !== true){
59204 this.applyConfig(config);
59208 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
59209 getPanelId : function(p){
59213 applyConfig : function(config){
59214 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59215 this.config = config;
59220 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
59221 * the width, for horizontal (north, south) the height.
59222 * @param {Number} newSize The new width or height
59224 resizeTo : function(newSize){
59225 var el = this.el ? this.el :
59226 (this.activePanel ? this.activePanel.getEl() : null);
59228 switch(this.position){
59231 el.setWidth(newSize);
59232 this.fireEvent("resized", this, newSize);
59236 el.setHeight(newSize);
59237 this.fireEvent("resized", this, newSize);
59243 getBox : function(){
59244 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
59247 getMargins : function(){
59248 return this.margins;
59251 updateBox : function(box){
59253 var el = this.activePanel.getEl();
59254 el.dom.style.left = box.x + "px";
59255 el.dom.style.top = box.y + "px";
59256 this.activePanel.setSize(box.width, box.height);
59260 * Returns the container element for this region.
59261 * @return {Roo.Element}
59263 getEl : function(){
59264 return this.activePanel;
59268 * Returns true if this region is currently visible.
59269 * @return {Boolean}
59271 isVisible : function(){
59272 return this.activePanel ? true : false;
59275 setActivePanel : function(panel){
59276 panel = this.getPanel(panel);
59277 if(this.activePanel && this.activePanel != panel){
59278 this.activePanel.setActiveState(false);
59279 this.activePanel.getEl().setLeftTop(-10000,-10000);
59281 this.activePanel = panel;
59282 panel.setActiveState(true);
59284 panel.setSize(this.box.width, this.box.height);
59286 this.fireEvent("panelactivated", this, panel);
59287 this.fireEvent("invalidated");
59291 * Show the specified panel.
59292 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
59293 * @return {Roo.ContentPanel} The shown panel or null
59295 showPanel : function(panel){
59296 if(panel = this.getPanel(panel)){
59297 this.setActivePanel(panel);
59303 * Get the active panel for this region.
59304 * @return {Roo.ContentPanel} The active panel or null
59306 getActivePanel : function(){
59307 return this.activePanel;
59311 * Add the passed ContentPanel(s)
59312 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59313 * @return {Roo.ContentPanel} The panel added (if only one was added)
59315 add : function(panel){
59316 if(arguments.length > 1){
59317 for(var i = 0, len = arguments.length; i < len; i++) {
59318 this.add(arguments[i]);
59322 if(this.hasPanel(panel)){
59323 this.showPanel(panel);
59326 var el = panel.getEl();
59327 if(el.dom.parentNode != this.mgr.el.dom){
59328 this.mgr.el.dom.appendChild(el.dom);
59330 if(panel.setRegion){
59331 panel.setRegion(this);
59333 this.panels.add(panel);
59334 el.setStyle("position", "absolute");
59335 if(!panel.background){
59336 this.setActivePanel(panel);
59337 if(this.config.initialSize && this.panels.getCount()==1){
59338 this.resizeTo(this.config.initialSize);
59341 this.fireEvent("paneladded", this, panel);
59346 * Returns true if the panel is in this region.
59347 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59348 * @return {Boolean}
59350 hasPanel : function(panel){
59351 if(typeof panel == "object"){ // must be panel obj
59352 panel = panel.getId();
59354 return this.getPanel(panel) ? true : false;
59358 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59359 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59360 * @param {Boolean} preservePanel Overrides the config preservePanel option
59361 * @return {Roo.ContentPanel} The panel that was removed
59363 remove : function(panel, preservePanel){
59364 panel = this.getPanel(panel);
59369 this.fireEvent("beforeremove", this, panel, e);
59370 if(e.cancel === true){
59373 var panelId = panel.getId();
59374 this.panels.removeKey(panelId);
59379 * Returns the panel specified or null if it's not in this region.
59380 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59381 * @return {Roo.ContentPanel}
59383 getPanel : function(id){
59384 if(typeof id == "object"){ // must be panel obj
59387 return this.panels.get(id);
59391 * Returns this regions position (north/south/east/west/center).
59394 getPosition: function(){
59395 return this.position;
59399 * Ext JS Library 1.1.1
59400 * Copyright(c) 2006-2007, Ext JS, LLC.
59402 * Originally Released Under LGPL - original licence link has changed is not relivant.
59405 * <script type="text/javascript">
59409 * @class Roo.LayoutRegion
59410 * @extends Roo.BasicLayoutRegion
59411 * This class represents a region in a layout manager.
59412 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
59413 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
59414 * @cfg {Boolean} floatable False to disable floating (defaults to true)
59415 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
59416 * @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})
59417 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
59418 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
59419 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
59420 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
59421 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
59422 * @cfg {String} title The title for the region (overrides panel titles)
59423 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
59424 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
59425 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
59426 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
59427 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
59428 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
59429 * the space available, similar to FireFox 1.5 tabs (defaults to false)
59430 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
59431 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
59432 * @cfg {Boolean} showPin True to show a pin button
59433 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
59434 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
59435 * @cfg {Boolean} disableTabTips True to disable tab tooltips
59436 * @cfg {Number} width For East/West panels
59437 * @cfg {Number} height For North/South panels
59438 * @cfg {Boolean} split To show the splitter
59439 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
59441 Roo.LayoutRegion = function(mgr, config, pos){
59442 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
59443 var dh = Roo.DomHelper;
59444 /** This region's container element
59445 * @type Roo.Element */
59446 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
59447 /** This region's title element
59448 * @type Roo.Element */
59450 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
59451 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
59452 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
59454 this.titleEl.enableDisplayMode();
59455 /** This region's title text element
59456 * @type HTMLElement */
59457 this.titleTextEl = this.titleEl.dom.firstChild;
59458 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
59459 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
59460 this.closeBtn.enableDisplayMode();
59461 this.closeBtn.on("click", this.closeClicked, this);
59462 this.closeBtn.hide();
59464 this.createBody(config);
59465 this.visible = true;
59466 this.collapsed = false;
59468 if(config.hideWhenEmpty){
59470 this.on("paneladded", this.validateVisibility, this);
59471 this.on("panelremoved", this.validateVisibility, this);
59473 this.applyConfig(config);
59476 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
59478 createBody : function(){
59479 /** This region's body element
59480 * @type Roo.Element */
59481 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
59484 applyConfig : function(c){
59485 if(c.collapsible && this.position != "center" && !this.collapsedEl){
59486 var dh = Roo.DomHelper;
59487 if(c.titlebar !== false){
59488 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
59489 this.collapseBtn.on("click", this.collapse, this);
59490 this.collapseBtn.enableDisplayMode();
59492 if(c.showPin === true || this.showPin){
59493 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
59494 this.stickBtn.enableDisplayMode();
59495 this.stickBtn.on("click", this.expand, this);
59496 this.stickBtn.hide();
59499 /** This region's collapsed element
59500 * @type Roo.Element */
59501 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
59502 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
59504 if(c.floatable !== false){
59505 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
59506 this.collapsedEl.on("click", this.collapseClick, this);
59509 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
59510 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
59511 id: "message", unselectable: "on", style:{"float":"left"}});
59512 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
59514 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
59515 this.expandBtn.on("click", this.expand, this);
59517 if(this.collapseBtn){
59518 this.collapseBtn.setVisible(c.collapsible == true);
59520 this.cmargins = c.cmargins || this.cmargins ||
59521 (this.position == "west" || this.position == "east" ?
59522 {top: 0, left: 2, right:2, bottom: 0} :
59523 {top: 2, left: 0, right:0, bottom: 2});
59524 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59525 this.bottomTabs = c.tabPosition != "top";
59526 this.autoScroll = c.autoScroll || false;
59527 if(this.autoScroll){
59528 this.bodyEl.setStyle("overflow", "auto");
59530 this.bodyEl.setStyle("overflow", "hidden");
59532 //if(c.titlebar !== false){
59533 if((!c.titlebar && !c.title) || c.titlebar === false){
59534 this.titleEl.hide();
59536 this.titleEl.show();
59538 this.titleTextEl.innerHTML = c.title;
59542 this.duration = c.duration || .30;
59543 this.slideDuration = c.slideDuration || .45;
59546 this.collapse(true);
59553 * Returns true if this region is currently visible.
59554 * @return {Boolean}
59556 isVisible : function(){
59557 return this.visible;
59561 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
59562 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
59564 setCollapsedTitle : function(title){
59565 title = title || " ";
59566 if(this.collapsedTitleTextEl){
59567 this.collapsedTitleTextEl.innerHTML = title;
59571 getBox : function(){
59573 if(!this.collapsed){
59574 b = this.el.getBox(false, true);
59576 b = this.collapsedEl.getBox(false, true);
59581 getMargins : function(){
59582 return this.collapsed ? this.cmargins : this.margins;
59585 highlight : function(){
59586 this.el.addClass("x-layout-panel-dragover");
59589 unhighlight : function(){
59590 this.el.removeClass("x-layout-panel-dragover");
59593 updateBox : function(box){
59595 if(!this.collapsed){
59596 this.el.dom.style.left = box.x + "px";
59597 this.el.dom.style.top = box.y + "px";
59598 this.updateBody(box.width, box.height);
59600 this.collapsedEl.dom.style.left = box.x + "px";
59601 this.collapsedEl.dom.style.top = box.y + "px";
59602 this.collapsedEl.setSize(box.width, box.height);
59605 this.tabs.autoSizeTabs();
59609 updateBody : function(w, h){
59611 this.el.setWidth(w);
59612 w -= this.el.getBorderWidth("rl");
59613 if(this.config.adjustments){
59614 w += this.config.adjustments[0];
59618 this.el.setHeight(h);
59619 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
59620 h -= this.el.getBorderWidth("tb");
59621 if(this.config.adjustments){
59622 h += this.config.adjustments[1];
59624 this.bodyEl.setHeight(h);
59626 h = this.tabs.syncHeight(h);
59629 if(this.panelSize){
59630 w = w !== null ? w : this.panelSize.width;
59631 h = h !== null ? h : this.panelSize.height;
59633 if(this.activePanel){
59634 var el = this.activePanel.getEl();
59635 w = w !== null ? w : el.getWidth();
59636 h = h !== null ? h : el.getHeight();
59637 this.panelSize = {width: w, height: h};
59638 this.activePanel.setSize(w, h);
59640 if(Roo.isIE && this.tabs){
59641 this.tabs.el.repaint();
59646 * Returns the container element for this region.
59647 * @return {Roo.Element}
59649 getEl : function(){
59654 * Hides this region.
59657 if(!this.collapsed){
59658 this.el.dom.style.left = "-2000px";
59661 this.collapsedEl.dom.style.left = "-2000px";
59662 this.collapsedEl.hide();
59664 this.visible = false;
59665 this.fireEvent("visibilitychange", this, false);
59669 * Shows this region if it was previously hidden.
59672 if(!this.collapsed){
59675 this.collapsedEl.show();
59677 this.visible = true;
59678 this.fireEvent("visibilitychange", this, true);
59681 closeClicked : function(){
59682 if(this.activePanel){
59683 this.remove(this.activePanel);
59687 collapseClick : function(e){
59689 e.stopPropagation();
59692 e.stopPropagation();
59698 * Collapses this region.
59699 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
59701 collapse : function(skipAnim, skipCheck){
59702 if(this.collapsed) {
59706 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
59708 this.collapsed = true;
59710 this.split.el.hide();
59712 if(this.config.animate && skipAnim !== true){
59713 this.fireEvent("invalidated", this);
59714 this.animateCollapse();
59716 this.el.setLocation(-20000,-20000);
59718 this.collapsedEl.show();
59719 this.fireEvent("collapsed", this);
59720 this.fireEvent("invalidated", this);
59726 animateCollapse : function(){
59731 * Expands this region if it was previously collapsed.
59732 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
59733 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
59735 expand : function(e, skipAnim){
59737 e.stopPropagation();
59739 if(!this.collapsed || this.el.hasActiveFx()) {
59743 this.afterSlideIn();
59746 this.collapsed = false;
59747 if(this.config.animate && skipAnim !== true){
59748 this.animateExpand();
59752 this.split.el.show();
59754 this.collapsedEl.setLocation(-2000,-2000);
59755 this.collapsedEl.hide();
59756 this.fireEvent("invalidated", this);
59757 this.fireEvent("expanded", this);
59761 animateExpand : function(){
59765 initTabs : function()
59767 this.bodyEl.setStyle("overflow", "hidden");
59768 var ts = new Roo.TabPanel(
59771 tabPosition: this.bottomTabs ? 'bottom' : 'top',
59772 disableTooltips: this.config.disableTabTips,
59773 toolbar : this.config.toolbar
59776 if(this.config.hideTabs){
59777 ts.stripWrap.setDisplayed(false);
59780 ts.resizeTabs = this.config.resizeTabs === true;
59781 ts.minTabWidth = this.config.minTabWidth || 40;
59782 ts.maxTabWidth = this.config.maxTabWidth || 250;
59783 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
59784 ts.monitorResize = false;
59785 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59786 ts.bodyEl.addClass('x-layout-tabs-body');
59787 this.panels.each(this.initPanelAsTab, this);
59790 initPanelAsTab : function(panel){
59791 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
59792 this.config.closeOnTab && panel.isClosable());
59793 if(panel.tabTip !== undefined){
59794 ti.setTooltip(panel.tabTip);
59796 ti.on("activate", function(){
59797 this.setActivePanel(panel);
59799 if(this.config.closeOnTab){
59800 ti.on("beforeclose", function(t, e){
59802 this.remove(panel);
59808 updatePanelTitle : function(panel, title){
59809 if(this.activePanel == panel){
59810 this.updateTitle(title);
59813 var ti = this.tabs.getTab(panel.getEl().id);
59815 if(panel.tabTip !== undefined){
59816 ti.setTooltip(panel.tabTip);
59821 updateTitle : function(title){
59822 if(this.titleTextEl && !this.config.title){
59823 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
59827 setActivePanel : function(panel){
59828 panel = this.getPanel(panel);
59829 if(this.activePanel && this.activePanel != panel){
59830 this.activePanel.setActiveState(false);
59832 this.activePanel = panel;
59833 panel.setActiveState(true);
59834 if(this.panelSize){
59835 panel.setSize(this.panelSize.width, this.panelSize.height);
59838 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
59840 this.updateTitle(panel.getTitle());
59842 this.fireEvent("invalidated", this);
59844 this.fireEvent("panelactivated", this, panel);
59848 * Shows the specified panel.
59849 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
59850 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
59852 showPanel : function(panel)
59854 panel = this.getPanel(panel);
59857 var tab = this.tabs.getTab(panel.getEl().id);
59858 if(tab.isHidden()){
59859 this.tabs.unhideTab(tab.id);
59863 this.setActivePanel(panel);
59870 * Get the active panel for this region.
59871 * @return {Roo.ContentPanel} The active panel or null
59873 getActivePanel : function(){
59874 return this.activePanel;
59877 validateVisibility : function(){
59878 if(this.panels.getCount() < 1){
59879 this.updateTitle(" ");
59880 this.closeBtn.hide();
59883 if(!this.isVisible()){
59890 * Adds the passed ContentPanel(s) to this region.
59891 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59892 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
59894 add : function(panel){
59895 if(arguments.length > 1){
59896 for(var i = 0, len = arguments.length; i < len; i++) {
59897 this.add(arguments[i]);
59901 if(this.hasPanel(panel)){
59902 this.showPanel(panel);
59905 panel.setRegion(this);
59906 this.panels.add(panel);
59907 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
59908 this.bodyEl.dom.appendChild(panel.getEl().dom);
59909 if(panel.background !== true){
59910 this.setActivePanel(panel);
59912 this.fireEvent("paneladded", this, panel);
59918 this.initPanelAsTab(panel);
59920 if(panel.background !== true){
59921 this.tabs.activate(panel.getEl().id);
59923 this.fireEvent("paneladded", this, panel);
59928 * Hides the tab for the specified panel.
59929 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59931 hidePanel : function(panel){
59932 if(this.tabs && (panel = this.getPanel(panel))){
59933 this.tabs.hideTab(panel.getEl().id);
59938 * Unhides the tab for a previously hidden panel.
59939 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59941 unhidePanel : function(panel){
59942 if(this.tabs && (panel = this.getPanel(panel))){
59943 this.tabs.unhideTab(panel.getEl().id);
59947 clearPanels : function(){
59948 while(this.panels.getCount() > 0){
59949 this.remove(this.panels.first());
59954 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59955 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59956 * @param {Boolean} preservePanel Overrides the config preservePanel option
59957 * @return {Roo.ContentPanel} The panel that was removed
59959 remove : function(panel, preservePanel){
59960 panel = this.getPanel(panel);
59965 this.fireEvent("beforeremove", this, panel, e);
59966 if(e.cancel === true){
59969 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
59970 var panelId = panel.getId();
59971 this.panels.removeKey(panelId);
59973 document.body.appendChild(panel.getEl().dom);
59976 this.tabs.removeTab(panel.getEl().id);
59977 }else if (!preservePanel){
59978 this.bodyEl.dom.removeChild(panel.getEl().dom);
59980 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
59981 var p = this.panels.first();
59982 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
59983 tempEl.appendChild(p.getEl().dom);
59984 this.bodyEl.update("");
59985 this.bodyEl.dom.appendChild(p.getEl().dom);
59987 this.updateTitle(p.getTitle());
59989 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59990 this.setActivePanel(p);
59992 panel.setRegion(null);
59993 if(this.activePanel == panel){
59994 this.activePanel = null;
59996 if(this.config.autoDestroy !== false && preservePanel !== true){
59997 try{panel.destroy();}catch(e){}
59999 this.fireEvent("panelremoved", this, panel);
60004 * Returns the TabPanel component used by this region
60005 * @return {Roo.TabPanel}
60007 getTabs : function(){
60011 createTool : function(parentEl, className){
60012 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
60013 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
60014 btn.addClassOnOver("x-layout-tools-button-over");
60019 * Ext JS Library 1.1.1
60020 * Copyright(c) 2006-2007, Ext JS, LLC.
60022 * Originally Released Under LGPL - original licence link has changed is not relivant.
60025 * <script type="text/javascript">
60031 * @class Roo.SplitLayoutRegion
60032 * @extends Roo.LayoutRegion
60033 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
60035 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
60036 this.cursor = cursor;
60037 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
60040 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
60041 splitTip : "Drag to resize.",
60042 collapsibleSplitTip : "Drag to resize. Double click to hide.",
60043 useSplitTips : false,
60045 applyConfig : function(config){
60046 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
60049 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
60050 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
60051 /** The SplitBar for this region
60052 * @type Roo.SplitBar */
60053 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
60054 this.split.on("moved", this.onSplitMove, this);
60055 this.split.useShim = config.useShim === true;
60056 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
60057 if(this.useSplitTips){
60058 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
60060 if(config.collapsible){
60061 this.split.el.on("dblclick", this.collapse, this);
60064 if(typeof config.minSize != "undefined"){
60065 this.split.minSize = config.minSize;
60067 if(typeof config.maxSize != "undefined"){
60068 this.split.maxSize = config.maxSize;
60070 if(config.hideWhenEmpty || config.hidden || config.collapsed){
60071 this.hideSplitter();
60076 getHMaxSize : function(){
60077 var cmax = this.config.maxSize || 10000;
60078 var center = this.mgr.getRegion("center");
60079 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
60082 getVMaxSize : function(){
60083 var cmax = this.config.maxSize || 10000;
60084 var center = this.mgr.getRegion("center");
60085 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
60088 onSplitMove : function(split, newSize){
60089 this.fireEvent("resized", this, newSize);
60093 * Returns the {@link Roo.SplitBar} for this region.
60094 * @return {Roo.SplitBar}
60096 getSplitBar : function(){
60101 this.hideSplitter();
60102 Roo.SplitLayoutRegion.superclass.hide.call(this);
60105 hideSplitter : function(){
60107 this.split.el.setLocation(-2000,-2000);
60108 this.split.el.hide();
60114 this.split.el.show();
60116 Roo.SplitLayoutRegion.superclass.show.call(this);
60119 beforeSlide: function(){
60120 if(Roo.isGecko){// firefox overflow auto bug workaround
60121 this.bodyEl.clip();
60123 this.tabs.bodyEl.clip();
60125 if(this.activePanel){
60126 this.activePanel.getEl().clip();
60128 if(this.activePanel.beforeSlide){
60129 this.activePanel.beforeSlide();
60135 afterSlide : function(){
60136 if(Roo.isGecko){// firefox overflow auto bug workaround
60137 this.bodyEl.unclip();
60139 this.tabs.bodyEl.unclip();
60141 if(this.activePanel){
60142 this.activePanel.getEl().unclip();
60143 if(this.activePanel.afterSlide){
60144 this.activePanel.afterSlide();
60150 initAutoHide : function(){
60151 if(this.autoHide !== false){
60152 if(!this.autoHideHd){
60153 var st = new Roo.util.DelayedTask(this.slideIn, this);
60154 this.autoHideHd = {
60155 "mouseout": function(e){
60156 if(!e.within(this.el, true)){
60160 "mouseover" : function(e){
60166 this.el.on(this.autoHideHd);
60170 clearAutoHide : function(){
60171 if(this.autoHide !== false){
60172 this.el.un("mouseout", this.autoHideHd.mouseout);
60173 this.el.un("mouseover", this.autoHideHd.mouseover);
60177 clearMonitor : function(){
60178 Roo.get(document).un("click", this.slideInIf, this);
60181 // these names are backwards but not changed for compat
60182 slideOut : function(){
60183 if(this.isSlid || this.el.hasActiveFx()){
60186 this.isSlid = true;
60187 if(this.collapseBtn){
60188 this.collapseBtn.hide();
60190 this.closeBtnState = this.closeBtn.getStyle('display');
60191 this.closeBtn.hide();
60193 this.stickBtn.show();
60196 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
60197 this.beforeSlide();
60198 this.el.setStyle("z-index", 10001);
60199 this.el.slideIn(this.getSlideAnchor(), {
60200 callback: function(){
60202 this.initAutoHide();
60203 Roo.get(document).on("click", this.slideInIf, this);
60204 this.fireEvent("slideshow", this);
60211 afterSlideIn : function(){
60212 this.clearAutoHide();
60213 this.isSlid = false;
60214 this.clearMonitor();
60215 this.el.setStyle("z-index", "");
60216 if(this.collapseBtn){
60217 this.collapseBtn.show();
60219 this.closeBtn.setStyle('display', this.closeBtnState);
60221 this.stickBtn.hide();
60223 this.fireEvent("slidehide", this);
60226 slideIn : function(cb){
60227 if(!this.isSlid || this.el.hasActiveFx()){
60231 this.isSlid = false;
60232 this.beforeSlide();
60233 this.el.slideOut(this.getSlideAnchor(), {
60234 callback: function(){
60235 this.el.setLeftTop(-10000, -10000);
60237 this.afterSlideIn();
60245 slideInIf : function(e){
60246 if(!e.within(this.el)){
60251 animateCollapse : function(){
60252 this.beforeSlide();
60253 this.el.setStyle("z-index", 20000);
60254 var anchor = this.getSlideAnchor();
60255 this.el.slideOut(anchor, {
60256 callback : function(){
60257 this.el.setStyle("z-index", "");
60258 this.collapsedEl.slideIn(anchor, {duration:.3});
60260 this.el.setLocation(-10000,-10000);
60262 this.fireEvent("collapsed", this);
60269 animateExpand : function(){
60270 this.beforeSlide();
60271 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
60272 this.el.setStyle("z-index", 20000);
60273 this.collapsedEl.hide({
60276 this.el.slideIn(this.getSlideAnchor(), {
60277 callback : function(){
60278 this.el.setStyle("z-index", "");
60281 this.split.el.show();
60283 this.fireEvent("invalidated", this);
60284 this.fireEvent("expanded", this);
60312 getAnchor : function(){
60313 return this.anchors[this.position];
60316 getCollapseAnchor : function(){
60317 return this.canchors[this.position];
60320 getSlideAnchor : function(){
60321 return this.sanchors[this.position];
60324 getAlignAdj : function(){
60325 var cm = this.cmargins;
60326 switch(this.position){
60342 getExpandAdj : function(){
60343 var c = this.collapsedEl, cm = this.cmargins;
60344 switch(this.position){
60346 return [-(cm.right+c.getWidth()+cm.left), 0];
60349 return [cm.right+c.getWidth()+cm.left, 0];
60352 return [0, -(cm.top+cm.bottom+c.getHeight())];
60355 return [0, cm.top+cm.bottom+c.getHeight()];
60361 * Ext JS Library 1.1.1
60362 * Copyright(c) 2006-2007, Ext JS, LLC.
60364 * Originally Released Under LGPL - original licence link has changed is not relivant.
60367 * <script type="text/javascript">
60370 * These classes are private internal classes
60372 Roo.CenterLayoutRegion = function(mgr, config){
60373 Roo.LayoutRegion.call(this, mgr, config, "center");
60374 this.visible = true;
60375 this.minWidth = config.minWidth || 20;
60376 this.minHeight = config.minHeight || 20;
60379 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
60381 // center panel can't be hidden
60385 // center panel can't be hidden
60388 getMinWidth: function(){
60389 return this.minWidth;
60392 getMinHeight: function(){
60393 return this.minHeight;
60398 Roo.NorthLayoutRegion = function(mgr, config){
60399 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
60401 this.split.placement = Roo.SplitBar.TOP;
60402 this.split.orientation = Roo.SplitBar.VERTICAL;
60403 this.split.el.addClass("x-layout-split-v");
60405 var size = config.initialSize || config.height;
60406 if(typeof size != "undefined"){
60407 this.el.setHeight(size);
60410 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
60411 orientation: Roo.SplitBar.VERTICAL,
60412 getBox : function(){
60413 if(this.collapsed){
60414 return this.collapsedEl.getBox();
60416 var box = this.el.getBox();
60418 box.height += this.split.el.getHeight();
60423 updateBox : function(box){
60424 if(this.split && !this.collapsed){
60425 box.height -= this.split.el.getHeight();
60426 this.split.el.setLeft(box.x);
60427 this.split.el.setTop(box.y+box.height);
60428 this.split.el.setWidth(box.width);
60430 if(this.collapsed){
60431 this.updateBody(box.width, null);
60433 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60437 Roo.SouthLayoutRegion = function(mgr, config){
60438 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
60440 this.split.placement = Roo.SplitBar.BOTTOM;
60441 this.split.orientation = Roo.SplitBar.VERTICAL;
60442 this.split.el.addClass("x-layout-split-v");
60444 var size = config.initialSize || config.height;
60445 if(typeof size != "undefined"){
60446 this.el.setHeight(size);
60449 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
60450 orientation: Roo.SplitBar.VERTICAL,
60451 getBox : function(){
60452 if(this.collapsed){
60453 return this.collapsedEl.getBox();
60455 var box = this.el.getBox();
60457 var sh = this.split.el.getHeight();
60464 updateBox : function(box){
60465 if(this.split && !this.collapsed){
60466 var sh = this.split.el.getHeight();
60469 this.split.el.setLeft(box.x);
60470 this.split.el.setTop(box.y-sh);
60471 this.split.el.setWidth(box.width);
60473 if(this.collapsed){
60474 this.updateBody(box.width, null);
60476 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60480 Roo.EastLayoutRegion = function(mgr, config){
60481 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
60483 this.split.placement = Roo.SplitBar.RIGHT;
60484 this.split.orientation = Roo.SplitBar.HORIZONTAL;
60485 this.split.el.addClass("x-layout-split-h");
60487 var size = config.initialSize || config.width;
60488 if(typeof size != "undefined"){
60489 this.el.setWidth(size);
60492 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
60493 orientation: Roo.SplitBar.HORIZONTAL,
60494 getBox : function(){
60495 if(this.collapsed){
60496 return this.collapsedEl.getBox();
60498 var box = this.el.getBox();
60500 var sw = this.split.el.getWidth();
60507 updateBox : function(box){
60508 if(this.split && !this.collapsed){
60509 var sw = this.split.el.getWidth();
60511 this.split.el.setLeft(box.x);
60512 this.split.el.setTop(box.y);
60513 this.split.el.setHeight(box.height);
60516 if(this.collapsed){
60517 this.updateBody(null, box.height);
60519 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60523 Roo.WestLayoutRegion = function(mgr, config){
60524 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
60526 this.split.placement = Roo.SplitBar.LEFT;
60527 this.split.orientation = Roo.SplitBar.HORIZONTAL;
60528 this.split.el.addClass("x-layout-split-h");
60530 var size = config.initialSize || config.width;
60531 if(typeof size != "undefined"){
60532 this.el.setWidth(size);
60535 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
60536 orientation: Roo.SplitBar.HORIZONTAL,
60537 getBox : function(){
60538 if(this.collapsed){
60539 return this.collapsedEl.getBox();
60541 var box = this.el.getBox();
60543 box.width += this.split.el.getWidth();
60548 updateBox : function(box){
60549 if(this.split && !this.collapsed){
60550 var sw = this.split.el.getWidth();
60552 this.split.el.setLeft(box.x+box.width);
60553 this.split.el.setTop(box.y);
60554 this.split.el.setHeight(box.height);
60556 if(this.collapsed){
60557 this.updateBody(null, box.height);
60559 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60564 * Ext JS Library 1.1.1
60565 * Copyright(c) 2006-2007, Ext JS, LLC.
60567 * Originally Released Under LGPL - original licence link has changed is not relivant.
60570 * <script type="text/javascript">
60575 * Private internal class for reading and applying state
60577 Roo.LayoutStateManager = function(layout){
60578 // default empty state
60587 Roo.LayoutStateManager.prototype = {
60588 init : function(layout, provider){
60589 this.provider = provider;
60590 var state = provider.get(layout.id+"-layout-state");
60592 var wasUpdating = layout.isUpdating();
60594 layout.beginUpdate();
60596 for(var key in state){
60597 if(typeof state[key] != "function"){
60598 var rstate = state[key];
60599 var r = layout.getRegion(key);
60602 r.resizeTo(rstate.size);
60604 if(rstate.collapsed == true){
60607 r.expand(null, true);
60613 layout.endUpdate();
60615 this.state = state;
60617 this.layout = layout;
60618 layout.on("regionresized", this.onRegionResized, this);
60619 layout.on("regioncollapsed", this.onRegionCollapsed, this);
60620 layout.on("regionexpanded", this.onRegionExpanded, this);
60623 storeState : function(){
60624 this.provider.set(this.layout.id+"-layout-state", this.state);
60627 onRegionResized : function(region, newSize){
60628 this.state[region.getPosition()].size = newSize;
60632 onRegionCollapsed : function(region){
60633 this.state[region.getPosition()].collapsed = true;
60637 onRegionExpanded : function(region){
60638 this.state[region.getPosition()].collapsed = false;
60643 * Ext JS Library 1.1.1
60644 * Copyright(c) 2006-2007, Ext JS, LLC.
60646 * Originally Released Under LGPL - original licence link has changed is not relivant.
60649 * <script type="text/javascript">
60652 * @class Roo.ContentPanel
60653 * @extends Roo.util.Observable
60654 * @children Roo.form.Form Roo.JsonView Roo.View
60655 * @parent Roo.BorderLayout Roo.LayoutDialog builder
60656 * A basic ContentPanel element.
60657 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
60658 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
60659 * @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
60660 * @cfg {Boolean} closable True if the panel can be closed/removed
60661 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
60662 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
60663 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
60664 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
60665 * @cfg {String} title The title for this panel
60666 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
60667 * @cfg {String} url Calls {@link #setUrl} with this value
60668 * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
60669 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
60670 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
60671 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
60672 * @cfg {String} style Extra style to add to the content panel
60673 * @cfg {Roo.menu.Menu} menu popup menu
60676 * Create a new ContentPanel.
60677 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
60678 * @param {String/Object} config A string to set only the title or a config object
60679 * @param {String} content (optional) Set the HTML content for this panel
60680 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
60682 Roo.ContentPanel = function(el, config, content){
60685 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
60689 if (config && config.parentLayout) {
60690 el = config.parentLayout.el.createChild();
60693 if(el.autoCreate){ // xtype is available if this is called from factory
60697 this.el = Roo.get(el);
60698 if(!this.el && config && config.autoCreate){
60699 if(typeof config.autoCreate == "object"){
60700 if(!config.autoCreate.id){
60701 config.autoCreate.id = config.id||el;
60703 this.el = Roo.DomHelper.append(document.body,
60704 config.autoCreate, true);
60706 this.el = Roo.DomHelper.append(document.body,
60707 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
60712 this.closable = false;
60713 this.loaded = false;
60714 this.active = false;
60715 if(typeof config == "string"){
60716 this.title = config;
60718 Roo.apply(this, config);
60721 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
60722 this.wrapEl = this.el.wrap();
60723 this.toolbar.container = this.el.insertSibling(false, 'before');
60724 this.toolbar = new Roo.Toolbar(this.toolbar);
60727 // xtype created footer. - not sure if will work as we normally have to render first..
60728 if (this.footer && !this.footer.el && this.footer.xtype) {
60729 if (!this.wrapEl) {
60730 this.wrapEl = this.el.wrap();
60733 this.footer.container = this.wrapEl.createChild();
60735 this.footer = Roo.factory(this.footer, Roo);
60740 this.resizeEl = Roo.get(this.resizeEl, true);
60742 this.resizeEl = this.el;
60744 // handle view.xtype
60752 * Fires when this panel is activated.
60753 * @param {Roo.ContentPanel} this
60757 * @event deactivate
60758 * Fires when this panel is activated.
60759 * @param {Roo.ContentPanel} this
60761 "deactivate" : true,
60765 * Fires when this panel is resized if fitToFrame is true.
60766 * @param {Roo.ContentPanel} this
60767 * @param {Number} width The width after any component adjustments
60768 * @param {Number} height The height after any component adjustments
60774 * Fires when this tab is created
60775 * @param {Roo.ContentPanel} this
60785 if(this.autoScroll){
60786 this.resizeEl.setStyle("overflow", "auto");
60788 // fix randome scrolling
60789 this.el.on('scroll', function() {
60790 Roo.log('fix random scolling');
60791 this.scrollTo('top',0);
60794 content = content || this.content;
60796 this.setContent(content);
60798 if(config && config.url){
60799 this.setUrl(this.url, this.params, this.loadOnce);
60804 Roo.ContentPanel.superclass.constructor.call(this);
60806 if (this.view && typeof(this.view.xtype) != 'undefined') {
60807 this.view.el = this.el.appendChild(document.createElement("div"));
60808 this.view = Roo.factory(this.view);
60809 this.view.render && this.view.render(false, '');
60813 this.fireEvent('render', this);
60816 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
60818 setRegion : function(region){
60819 this.region = region;
60821 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
60823 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
60828 * Returns the toolbar for this Panel if one was configured.
60829 * @return {Roo.Toolbar}
60831 getToolbar : function(){
60832 return this.toolbar;
60835 setActiveState : function(active){
60836 this.active = active;
60838 this.fireEvent("deactivate", this);
60840 this.fireEvent("activate", this);
60844 * Updates this panel's element
60845 * @param {String} content The new content
60846 * @param {Boolean} loadScripts (optional) true to look for and process scripts
60848 setContent : function(content, loadScripts){
60849 this.el.update(content, loadScripts);
60852 ignoreResize : function(w, h){
60853 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
60856 this.lastSize = {width: w, height: h};
60861 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
60862 * @return {Roo.UpdateManager} The UpdateManager
60864 getUpdateManager : function(){
60865 return this.el.getUpdateManager();
60868 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
60869 * @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:
60872 url: "your-url.php",
60873 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
60874 callback: yourFunction,
60875 scope: yourObject, //(optional scope)
60878 text: "Loading...",
60883 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
60884 * 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.
60885 * @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}
60886 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
60887 * @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.
60888 * @return {Roo.ContentPanel} this
60891 var um = this.el.getUpdateManager();
60892 um.update.apply(um, arguments);
60898 * 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.
60899 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
60900 * @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)
60901 * @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)
60902 * @return {Roo.UpdateManager} The UpdateManager
60904 setUrl : function(url, params, loadOnce){
60905 if(this.refreshDelegate){
60906 this.removeListener("activate", this.refreshDelegate);
60908 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
60909 this.on("activate", this.refreshDelegate);
60910 return this.el.getUpdateManager();
60913 _handleRefresh : function(url, params, loadOnce){
60914 if(!loadOnce || !this.loaded){
60915 var updater = this.el.getUpdateManager();
60916 updater.update(url, params, this._setLoaded.createDelegate(this));
60920 _setLoaded : function(){
60921 this.loaded = true;
60925 * Returns this panel's id
60928 getId : function(){
60933 * Returns this panel's element - used by regiosn to add.
60934 * @return {Roo.Element}
60936 getEl : function(){
60937 return this.wrapEl || this.el;
60940 adjustForComponents : function(width, height)
60942 //Roo.log('adjustForComponents ');
60943 if(this.resizeEl != this.el){
60944 width -= this.el.getFrameWidth('lr');
60945 height -= this.el.getFrameWidth('tb');
60948 var te = this.toolbar.getEl();
60949 height -= te.getHeight();
60950 te.setWidth(width);
60953 var te = this.footer.getEl();
60954 //Roo.log("footer:" + te.getHeight());
60956 height -= te.getHeight();
60957 te.setWidth(width);
60961 if(this.adjustments){
60962 width += this.adjustments[0];
60963 height += this.adjustments[1];
60965 return {"width": width, "height": height};
60968 setSize : function(width, height){
60969 if(this.fitToFrame && !this.ignoreResize(width, height)){
60970 if(this.fitContainer && this.resizeEl != this.el){
60971 this.el.setSize(width, height);
60973 var size = this.adjustForComponents(width, height);
60974 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
60975 this.fireEvent('resize', this, size.width, size.height);
60980 * Returns this panel's title
60983 getTitle : function(){
60988 * Set this panel's title
60989 * @param {String} title
60991 setTitle : function(title){
60992 this.title = title;
60994 this.region.updatePanelTitle(this, title);
60999 * Returns true is this panel was configured to be closable
61000 * @return {Boolean}
61002 isClosable : function(){
61003 return this.closable;
61006 beforeSlide : function(){
61008 this.resizeEl.clip();
61011 afterSlide : function(){
61013 this.resizeEl.unclip();
61017 * Force a content refresh from the URL specified in the {@link #setUrl} method.
61018 * Will fail silently if the {@link #setUrl} method has not been called.
61019 * This does not activate the panel, just updates its content.
61021 refresh : function(){
61022 if(this.refreshDelegate){
61023 this.loaded = false;
61024 this.refreshDelegate();
61029 * Destroys this panel
61031 destroy : function(){
61032 this.el.removeAllListeners();
61033 var tempEl = document.createElement("span");
61034 tempEl.appendChild(this.el.dom);
61035 tempEl.innerHTML = "";
61041 * form - if the content panel contains a form - this is a reference to it.
61042 * @type {Roo.form.Form}
61046 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
61047 * This contains a reference to it.
61053 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
61063 * @param {Object} cfg Xtype definition of item to add.
61066 addxtype : function(cfg) {
61067 if(cfg.xtype.match(/^UploadCropbox$/)) {
61069 this.cropbox = new Roo.factory(cfg);
61071 this.cropbox.render(this.el);
61073 return this.cropbox;
61076 if (cfg.xtype.match(/^Form$/)) {
61079 //if (this.footer) {
61080 // el = this.footer.container.insertSibling(false, 'before');
61082 el = this.el.createChild();
61085 this.form = new Roo.form.Form(cfg);
61088 if ( this.form.allItems.length) {
61089 this.form.render(el.dom);
61093 // should only have one of theses..
61094 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
61095 // views.. should not be just added - used named prop 'view''
61097 cfg.el = this.el.appendChild(document.createElement("div"));
61100 var ret = new Roo.factory(cfg);
61102 ret.render && ret.render(false, ''); // render blank..
61122 * @class Roo.GridPanel
61123 * @extends Roo.ContentPanel
61124 * @parent Roo.BorderLayout Roo.LayoutDialog builder
61126 * Create a new GridPanel.
61127 * @cfg {Roo.grid.Grid} grid The grid for this panel
61129 Roo.GridPanel = function(grid, config){
61131 // universal ctor...
61132 if (typeof(grid.grid) != 'undefined') {
61134 grid = config.grid;
61136 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
61137 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
61139 this.wrapper.dom.appendChild(grid.getGridEl().dom);
61141 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
61144 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
61146 // xtype created footer. - not sure if will work as we normally have to render first..
61147 if (this.footer && !this.footer.el && this.footer.xtype) {
61149 this.footer.container = this.grid.getView().getFooterPanel(true);
61150 this.footer.dataSource = this.grid.dataSource;
61151 this.footer = Roo.factory(this.footer, Roo);
61155 grid.monitorWindowResize = false; // turn off autosizing
61156 grid.autoHeight = false;
61157 grid.autoWidth = false;
61159 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
61162 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
61163 getId : function(){
61164 return this.grid.id;
61168 * Returns the grid for this panel
61169 * @return {Roo.grid.Grid}
61171 getGrid : function(){
61175 setSize : function(width, height){
61176 if(!this.ignoreResize(width, height)){
61177 var grid = this.grid;
61178 var size = this.adjustForComponents(width, height);
61179 grid.getGridEl().setSize(size.width, size.height);
61184 beforeSlide : function(){
61185 this.grid.getView().scroller.clip();
61188 afterSlide : function(){
61189 this.grid.getView().scroller.unclip();
61192 destroy : function(){
61193 this.grid.destroy();
61195 Roo.GridPanel.superclass.destroy.call(this);
61201 * @class Roo.NestedLayoutPanel
61202 * @extends Roo.ContentPanel
61203 * @parent Roo.BorderLayout Roo.LayoutDialog builder
61204 * @cfg {Roo.BorderLayout} layout [required] The layout for this panel
61208 * Create a new NestedLayoutPanel.
61211 * @param {Roo.BorderLayout} layout [required] The layout for this panel
61212 * @param {String/Object} config A string to set only the title or a config object
61214 Roo.NestedLayoutPanel = function(layout, config)
61216 // construct with only one argument..
61217 /* FIXME - implement nicer consturctors
61218 if (layout.layout) {
61220 layout = config.layout;
61221 delete config.layout;
61223 if (layout.xtype && !layout.getEl) {
61224 // then layout needs constructing..
61225 layout = Roo.factory(layout, Roo);
61230 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
61232 layout.monitorWindowResize = false; // turn off autosizing
61233 this.layout = layout;
61234 this.layout.getEl().addClass("x-layout-nested-layout");
61241 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
61245 setSize : function(width, height){
61246 if(!this.ignoreResize(width, height)){
61247 var size = this.adjustForComponents(width, height);
61248 var el = this.layout.getEl();
61249 el.setSize(size.width, size.height);
61250 var touch = el.dom.offsetWidth;
61251 this.layout.layout();
61252 // ie requires a double layout on the first pass
61253 if(Roo.isIE && !this.initialized){
61254 this.initialized = true;
61255 this.layout.layout();
61260 // activate all subpanels if not currently active..
61262 setActiveState : function(active){
61263 this.active = active;
61265 this.fireEvent("deactivate", this);
61269 this.fireEvent("activate", this);
61270 // not sure if this should happen before or after..
61271 if (!this.layout) {
61272 return; // should not happen..
61275 for (var r in this.layout.regions) {
61276 reg = this.layout.getRegion(r);
61277 if (reg.getActivePanel()) {
61278 //reg.showPanel(reg.getActivePanel()); // force it to activate..
61279 reg.setActivePanel(reg.getActivePanel());
61282 if (!reg.panels.length) {
61285 reg.showPanel(reg.getPanel(0));
61294 * Returns the nested BorderLayout for this panel
61295 * @return {Roo.BorderLayout}
61297 getLayout : function(){
61298 return this.layout;
61302 * Adds a xtype elements to the layout of the nested panel
61306 xtype : 'ContentPanel',
61313 xtype : 'NestedLayoutPanel',
61319 items : [ ... list of content panels or nested layout panels.. ]
61323 * @param {Object} cfg Xtype definition of item to add.
61325 addxtype : function(cfg) {
61326 return this.layout.addxtype(cfg);
61331 Roo.ScrollPanel = function(el, config, content){
61332 config = config || {};
61333 config.fitToFrame = true;
61334 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
61336 this.el.dom.style.overflow = "hidden";
61337 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
61338 this.el.removeClass("x-layout-inactive-content");
61339 this.el.on("mousewheel", this.onWheel, this);
61341 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
61342 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
61343 up.unselectable(); down.unselectable();
61344 up.on("click", this.scrollUp, this);
61345 down.on("click", this.scrollDown, this);
61346 up.addClassOnOver("x-scroller-btn-over");
61347 down.addClassOnOver("x-scroller-btn-over");
61348 up.addClassOnClick("x-scroller-btn-click");
61349 down.addClassOnClick("x-scroller-btn-click");
61350 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
61352 this.resizeEl = this.el;
61353 this.el = wrap; this.up = up; this.down = down;
61356 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
61358 wheelIncrement : 5,
61359 scrollUp : function(){
61360 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
61363 scrollDown : function(){
61364 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
61367 afterScroll : function(){
61368 var el = this.resizeEl;
61369 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
61370 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61371 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61374 setSize : function(){
61375 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
61376 this.afterScroll();
61379 onWheel : function(e){
61380 var d = e.getWheelDelta();
61381 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
61382 this.afterScroll();
61386 setContent : function(content, loadScripts){
61387 this.resizeEl.update(content, loadScripts);
61395 * @class Roo.TreePanel
61396 * @extends Roo.ContentPanel
61397 * @parent Roo.BorderLayout Roo.LayoutDialog builder
61398 * Treepanel component
61401 * Create a new TreePanel. - defaults to fit/scoll contents.
61402 * @param {String/Object} config A string to set only the panel's title, or a config object
61404 Roo.TreePanel = function(config){
61405 var el = config.el;
61406 var tree = config.tree;
61407 delete config.tree;
61408 delete config.el; // hopefull!
61410 // wrapper for IE7 strict & safari scroll issue
61412 var treeEl = el.createChild();
61413 config.resizeEl = treeEl;
61417 Roo.TreePanel.superclass.constructor.call(this, el, config);
61420 this.tree = new Roo.tree.TreePanel(treeEl , tree);
61421 //console.log(tree);
61422 this.on('activate', function()
61424 if (this.tree.rendered) {
61427 //console.log('render tree');
61428 this.tree.render();
61430 // this should not be needed.. - it's actually the 'el' that resizes?
61431 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
61433 //this.on('resize', function (cp, w, h) {
61434 // this.tree.innerCt.setWidth(w);
61435 // this.tree.innerCt.setHeight(h);
61436 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
61443 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
61447 * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
61454 * Ext JS Library 1.1.1
61455 * Copyright(c) 2006-2007, Ext JS, LLC.
61457 * Originally Released Under LGPL - original licence link has changed is not relivant.
61460 * <script type="text/javascript">
61465 * @class Roo.ReaderLayout
61466 * @extends Roo.BorderLayout
61467 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
61468 * center region containing two nested regions (a top one for a list view and one for item preview below),
61469 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
61470 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
61471 * expedites the setup of the overall layout and regions for this common application style.
61474 var reader = new Roo.ReaderLayout();
61475 var CP = Roo.ContentPanel; // shortcut for adding
61477 reader.beginUpdate();
61478 reader.add("north", new CP("north", "North"));
61479 reader.add("west", new CP("west", {title: "West"}));
61480 reader.add("east", new CP("east", {title: "East"}));
61482 reader.regions.listView.add(new CP("listView", "List"));
61483 reader.regions.preview.add(new CP("preview", "Preview"));
61484 reader.endUpdate();
61487 * Create a new ReaderLayout
61488 * @param {Object} config Configuration options
61489 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
61490 * document.body if omitted)
61492 Roo.ReaderLayout = function(config, renderTo){
61493 var c = config || {size:{}};
61494 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
61495 north: c.north !== false ? Roo.apply({
61499 }, c.north) : false,
61500 west: c.west !== false ? Roo.apply({
61508 margins:{left:5,right:0,bottom:5,top:5},
61509 cmargins:{left:5,right:5,bottom:5,top:5}
61510 }, c.west) : false,
61511 east: c.east !== false ? Roo.apply({
61519 margins:{left:0,right:5,bottom:5,top:5},
61520 cmargins:{left:5,right:5,bottom:5,top:5}
61521 }, c.east) : false,
61522 center: Roo.apply({
61523 tabPosition: 'top',
61527 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
61531 this.el.addClass('x-reader');
61533 this.beginUpdate();
61535 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
61536 south: c.preview !== false ? Roo.apply({
61543 cmargins:{top:5,left:0, right:0, bottom:0}
61544 }, c.preview) : false,
61545 center: Roo.apply({
61551 this.add('center', new Roo.NestedLayoutPanel(inner,
61552 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
61556 this.regions.preview = inner.getRegion('south');
61557 this.regions.listView = inner.getRegion('center');
61560 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
61562 * Ext JS Library 1.1.1
61563 * Copyright(c) 2006-2007, Ext JS, LLC.
61565 * Originally Released Under LGPL - original licence link has changed is not relivant.
61568 * <script type="text/javascript">
61572 * @class Roo.grid.Grid
61573 * @extends Roo.util.Observable
61574 * This class represents the primary interface of a component based grid control.
61575 * <br><br>Usage:<pre><code>
61576 var grid = new Roo.grid.Grid("my-container-id", {
61579 selModel: mySelectionModel,
61580 autoSizeColumns: true,
61581 monitorWindowResize: false,
61582 trackMouseOver: true
61587 * <b>Common Problems:</b><br/>
61588 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
61589 * element will correct this<br/>
61590 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
61591 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
61592 * are unpredictable.<br/>
61593 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
61594 * grid to calculate dimensions/offsets.<br/>
61596 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61597 * The container MUST have some type of size defined for the grid to fill. The container will be
61598 * automatically set to position relative if it isn't already.
61599 * @param {Object} config A config object that sets properties on this grid.
61601 Roo.grid.Grid = function(container, config){
61602 // initialize the container
61603 this.container = Roo.get(container);
61604 this.container.update("");
61605 this.container.setStyle("overflow", "hidden");
61606 this.container.addClass('x-grid-container');
61608 this.id = this.container.id;
61610 Roo.apply(this, config);
61611 // check and correct shorthanded configs
61613 this.dataSource = this.ds;
61617 this.colModel = this.cm;
61621 this.selModel = this.sm;
61625 if (this.selModel) {
61626 this.selModel = Roo.factory(this.selModel, Roo.grid);
61627 this.sm = this.selModel;
61628 this.sm.xmodule = this.xmodule || false;
61630 if (typeof(this.colModel.config) == 'undefined') {
61631 this.colModel = new Roo.grid.ColumnModel(this.colModel);
61632 this.cm = this.colModel;
61633 this.cm.xmodule = this.xmodule || false;
61635 if (this.dataSource) {
61636 this.dataSource= Roo.factory(this.dataSource, Roo.data);
61637 this.ds = this.dataSource;
61638 this.ds.xmodule = this.xmodule || false;
61645 this.container.setWidth(this.width);
61649 this.container.setHeight(this.height);
61656 * The raw click event for the entire grid.
61657 * @param {Roo.EventObject} e
61662 * The raw dblclick event for the entire grid.
61663 * @param {Roo.EventObject} e
61667 * @event contextmenu
61668 * The raw contextmenu event for the entire grid.
61669 * @param {Roo.EventObject} e
61671 "contextmenu" : true,
61674 * The raw mousedown event for the entire grid.
61675 * @param {Roo.EventObject} e
61677 "mousedown" : true,
61680 * The raw mouseup event for the entire grid.
61681 * @param {Roo.EventObject} e
61686 * The raw mouseover event for the entire grid.
61687 * @param {Roo.EventObject} e
61689 "mouseover" : true,
61692 * The raw mouseout event for the entire grid.
61693 * @param {Roo.EventObject} e
61698 * The raw keypress event for the entire grid.
61699 * @param {Roo.EventObject} e
61704 * The raw keydown event for the entire grid.
61705 * @param {Roo.EventObject} e
61713 * Fires when a cell is clicked
61714 * @param {Grid} this
61715 * @param {Number} rowIndex
61716 * @param {Number} columnIndex
61717 * @param {Roo.EventObject} e
61719 "cellclick" : true,
61721 * @event celldblclick
61722 * Fires when a cell is double clicked
61723 * @param {Grid} this
61724 * @param {Number} rowIndex
61725 * @param {Number} columnIndex
61726 * @param {Roo.EventObject} e
61728 "celldblclick" : true,
61731 * Fires when a row is clicked
61732 * @param {Grid} this
61733 * @param {Number} rowIndex
61734 * @param {Roo.EventObject} e
61738 * @event rowdblclick
61739 * Fires when a row is double clicked
61740 * @param {Grid} this
61741 * @param {Number} rowIndex
61742 * @param {Roo.EventObject} e
61744 "rowdblclick" : true,
61746 * @event headerclick
61747 * Fires when a header is clicked
61748 * @param {Grid} this
61749 * @param {Number} columnIndex
61750 * @param {Roo.EventObject} e
61752 "headerclick" : true,
61754 * @event headerdblclick
61755 * Fires when a header cell is double clicked
61756 * @param {Grid} this
61757 * @param {Number} columnIndex
61758 * @param {Roo.EventObject} e
61760 "headerdblclick" : true,
61762 * @event rowcontextmenu
61763 * Fires when a row is right clicked
61764 * @param {Grid} this
61765 * @param {Number} rowIndex
61766 * @param {Roo.EventObject} e
61768 "rowcontextmenu" : true,
61770 * @event cellcontextmenu
61771 * Fires when a cell is right clicked
61772 * @param {Grid} this
61773 * @param {Number} rowIndex
61774 * @param {Number} cellIndex
61775 * @param {Roo.EventObject} e
61777 "cellcontextmenu" : true,
61779 * @event headercontextmenu
61780 * Fires when a header is right clicked
61781 * @param {Grid} this
61782 * @param {Number} columnIndex
61783 * @param {Roo.EventObject} e
61785 "headercontextmenu" : true,
61787 * @event bodyscroll
61788 * Fires when the body element is scrolled
61789 * @param {Number} scrollLeft
61790 * @param {Number} scrollTop
61792 "bodyscroll" : true,
61794 * @event columnresize
61795 * Fires when the user resizes a column
61796 * @param {Number} columnIndex
61797 * @param {Number} newSize
61799 "columnresize" : true,
61801 * @event columnmove
61802 * Fires when the user moves a column
61803 * @param {Number} oldIndex
61804 * @param {Number} newIndex
61806 "columnmove" : true,
61809 * Fires when row(s) start being dragged
61810 * @param {Grid} this
61811 * @param {Roo.GridDD} dd The drag drop object
61812 * @param {event} e The raw browser event
61814 "startdrag" : true,
61817 * Fires when a drag operation is complete
61818 * @param {Grid} this
61819 * @param {Roo.GridDD} dd The drag drop object
61820 * @param {event} e The raw browser event
61825 * Fires when dragged row(s) are dropped on a valid DD target
61826 * @param {Grid} this
61827 * @param {Roo.GridDD} dd The drag drop object
61828 * @param {String} targetId The target drag drop object
61829 * @param {event} e The raw browser event
61834 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61835 * @param {Grid} this
61836 * @param {Roo.GridDD} dd The drag drop object
61837 * @param {String} targetId The target drag drop object
61838 * @param {event} e The raw browser event
61843 * Fires when the dragged row(s) first cross another DD target while being dragged
61844 * @param {Grid} this
61845 * @param {Roo.GridDD} dd The drag drop object
61846 * @param {String} targetId The target drag drop object
61847 * @param {event} e The raw browser event
61849 "dragenter" : true,
61852 * Fires when the dragged row(s) leave another DD target while being dragged
61853 * @param {Grid} this
61854 * @param {Roo.GridDD} dd The drag drop object
61855 * @param {String} targetId The target drag drop object
61856 * @param {event} e The raw browser event
61861 * Fires when a row is rendered, so you can change add a style to it.
61862 * @param {GridView} gridview The grid view
61863 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
61869 * Fires when the grid is rendered
61870 * @param {Grid} grid
61875 Roo.grid.Grid.superclass.constructor.call(this);
61877 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
61880 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
61883 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
61886 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
61889 * @cfg {Roo.data.Store} ds The data store for the grid
61892 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
61895 * @cfg {String} ddGroup - drag drop group.
61898 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
61902 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
61904 minColumnWidth : 25,
61907 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
61908 * <b>on initial render.</b> It is more efficient to explicitly size the columns
61909 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
61911 autoSizeColumns : false,
61914 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
61916 autoSizeHeaders : true,
61919 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
61921 monitorWindowResize : true,
61924 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
61925 * rows measured to get a columns size. Default is 0 (all rows).
61927 maxRowsToMeasure : 0,
61930 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
61932 trackMouseOver : true,
61935 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
61938 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
61942 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
61944 enableDragDrop : false,
61947 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
61949 enableColumnMove : true,
61952 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
61954 enableColumnHide : true,
61957 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
61959 enableRowHeightSync : false,
61962 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
61967 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
61969 autoHeight : false,
61972 * @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.
61974 autoExpandColumn : false,
61977 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
61980 autoExpandMin : 50,
61983 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
61985 autoExpandMax : 1000,
61988 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
61993 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
61997 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
62001 * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
62003 sortColMenu : false,
62009 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
62010 * of a fixed width. Default is false.
62013 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
62018 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
62019 * %0 is replaced with the number of selected rows.
62021 ddText : "{0} selected row{1}",
62025 * Called once after all setup has been completed and the grid is ready to be rendered.
62026 * @return {Roo.grid.Grid} this
62028 render : function()
62030 var c = this.container;
62031 // try to detect autoHeight/width mode
62032 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
62033 this.autoHeight = true;
62035 var view = this.getView();
62038 c.on("click", this.onClick, this);
62039 c.on("dblclick", this.onDblClick, this);
62040 c.on("contextmenu", this.onContextMenu, this);
62041 c.on("keydown", this.onKeyDown, this);
62043 c.on("touchstart", this.onTouchStart, this);
62046 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
62048 this.getSelectionModel().init(this);
62053 this.loadMask = new Roo.LoadMask(this.container,
62054 Roo.apply({store:this.dataSource}, this.loadMask));
62058 if (this.toolbar && this.toolbar.xtype) {
62059 this.toolbar.container = this.getView().getHeaderPanel(true);
62060 this.toolbar = new Roo.Toolbar(this.toolbar);
62062 if (this.footer && this.footer.xtype) {
62063 this.footer.dataSource = this.getDataSource();
62064 this.footer.container = this.getView().getFooterPanel(true);
62065 this.footer = Roo.factory(this.footer, Roo);
62067 if (this.dropTarget && this.dropTarget.xtype) {
62068 delete this.dropTarget.xtype;
62069 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
62073 this.rendered = true;
62074 this.fireEvent('render', this);
62079 * Reconfigures the grid to use a different Store and Column Model.
62080 * The View will be bound to the new objects and refreshed.
62081 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
62082 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
62084 reconfigure : function(dataSource, colModel){
62086 this.loadMask.destroy();
62087 this.loadMask = new Roo.LoadMask(this.container,
62088 Roo.apply({store:dataSource}, this.loadMask));
62090 this.view.bind(dataSource, colModel);
62091 this.dataSource = dataSource;
62092 this.colModel = colModel;
62093 this.view.refresh(true);
62097 * Add's a column, default at the end..
62099 * @param {int} position to add (default end)
62100 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
62102 addColumns : function(pos, ar)
62105 for (var i =0;i< ar.length;i++) {
62107 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
62108 this.cm.lookup[cfg.id] = cfg;
62112 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
62113 pos = this.cm.config.length; //this.cm.config.push(cfg);
62115 pos = Math.max(0,pos);
62118 this.cm.config.splice.apply(this.cm.config, ar);
62122 this.view.generateRules(this.cm);
62123 this.view.refresh(true);
62131 onKeyDown : function(e){
62132 this.fireEvent("keydown", e);
62136 * Destroy this grid.
62137 * @param {Boolean} removeEl True to remove the element
62139 destroy : function(removeEl, keepListeners){
62141 this.loadMask.destroy();
62143 var c = this.container;
62144 c.removeAllListeners();
62145 this.view.destroy();
62146 this.colModel.purgeListeners();
62147 if(!keepListeners){
62148 this.purgeListeners();
62151 if(removeEl === true){
62157 processEvent : function(name, e){
62158 // does this fire select???
62159 //Roo.log('grid:processEvent ' + name);
62161 if (name != 'touchstart' ) {
62162 this.fireEvent(name, e);
62165 var t = e.getTarget();
62167 var header = v.findHeaderIndex(t);
62168 if(header !== false){
62169 var ename = name == 'touchstart' ? 'click' : name;
62171 this.fireEvent("header" + ename, this, header, e);
62173 var row = v.findRowIndex(t);
62174 var cell = v.findCellIndex(t);
62175 if (name == 'touchstart') {
62176 // first touch is always a click.
62177 // hopefull this happens after selection is updated.?
62180 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
62181 var cs = this.selModel.getSelectedCell();
62182 if (row == cs[0] && cell == cs[1]){
62186 if (typeof(this.selModel.getSelections) != 'undefined') {
62187 var cs = this.selModel.getSelections();
62188 var ds = this.dataSource;
62189 if (cs.length == 1 && ds.getAt(row) == cs[0]){
62200 this.fireEvent("row" + name, this, row, e);
62201 if(cell !== false){
62202 this.fireEvent("cell" + name, this, row, cell, e);
62209 onClick : function(e){
62210 this.processEvent("click", e);
62213 onTouchStart : function(e){
62214 this.processEvent("touchstart", e);
62218 onContextMenu : function(e, t){
62219 this.processEvent("contextmenu", e);
62223 onDblClick : function(e){
62224 this.processEvent("dblclick", e);
62228 walkCells : function(row, col, step, fn, scope){
62229 var cm = this.colModel, clen = cm.getColumnCount();
62230 var ds = this.dataSource, rlen = ds.getCount(), first = true;
62242 if(fn.call(scope || this, row, col, cm) === true){
62260 if(fn.call(scope || this, row, col, cm) === true){
62272 getSelections : function(){
62273 return this.selModel.getSelections();
62277 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
62278 * but if manual update is required this method will initiate it.
62280 autoSize : function(){
62282 this.view.layout();
62283 if(this.view.adjustForScroll){
62284 this.view.adjustForScroll();
62290 * Returns the grid's underlying element.
62291 * @return {Element} The element
62293 getGridEl : function(){
62294 return this.container;
62297 // private for compatibility, overridden by editor grid
62298 stopEditing : function(){},
62301 * Returns the grid's SelectionModel.
62302 * @return {SelectionModel}
62304 getSelectionModel : function(){
62305 if(!this.selModel){
62306 this.selModel = new Roo.grid.RowSelectionModel();
62308 return this.selModel;
62312 * Returns the grid's DataSource.
62313 * @return {DataSource}
62315 getDataSource : function(){
62316 return this.dataSource;
62320 * Returns the grid's ColumnModel.
62321 * @return {ColumnModel}
62323 getColumnModel : function(){
62324 return this.colModel;
62328 * Returns the grid's GridView object.
62329 * @return {GridView}
62331 getView : function(){
62333 this.view = new Roo.grid.GridView(this.viewConfig);
62334 this.relayEvents(this.view, [
62335 "beforerowremoved", "beforerowsinserted",
62336 "beforerefresh", "rowremoved",
62337 "rowsinserted", "rowupdated" ,"refresh"
62343 * Called to get grid's drag proxy text, by default returns this.ddText.
62344 * Override this to put something different in the dragged text.
62347 getDragDropText : function(){
62348 var count = this.selModel.getCount();
62349 return String.format(this.ddText, count, count == 1 ? '' : 's');
62354 * Ext JS Library 1.1.1
62355 * Copyright(c) 2006-2007, Ext JS, LLC.
62357 * Originally Released Under LGPL - original licence link has changed is not relivant.
62360 * <script type="text/javascript">
62363 * @class Roo.grid.AbstractGridView
62364 * @extends Roo.util.Observable
62366 * Abstract base class for grid Views
62369 Roo.grid.AbstractGridView = function(){
62373 "beforerowremoved" : true,
62374 "beforerowsinserted" : true,
62375 "beforerefresh" : true,
62376 "rowremoved" : true,
62377 "rowsinserted" : true,
62378 "rowupdated" : true,
62381 Roo.grid.AbstractGridView.superclass.constructor.call(this);
62384 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
62385 rowClass : "x-grid-row",
62386 cellClass : "x-grid-cell",
62387 tdClass : "x-grid-td",
62388 hdClass : "x-grid-hd",
62389 splitClass : "x-grid-hd-split",
62391 init: function(grid){
62393 var cid = this.grid.getGridEl().id;
62394 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
62395 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
62396 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
62397 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
62400 getColumnRenderers : function(){
62401 var renderers = [];
62402 var cm = this.grid.colModel;
62403 var colCount = cm.getColumnCount();
62404 for(var i = 0; i < colCount; i++){
62405 renderers[i] = cm.getRenderer(i);
62410 getColumnIds : function(){
62412 var cm = this.grid.colModel;
62413 var colCount = cm.getColumnCount();
62414 for(var i = 0; i < colCount; i++){
62415 ids[i] = cm.getColumnId(i);
62420 getDataIndexes : function(){
62421 if(!this.indexMap){
62422 this.indexMap = this.buildIndexMap();
62424 return this.indexMap.colToData;
62427 getColumnIndexByDataIndex : function(dataIndex){
62428 if(!this.indexMap){
62429 this.indexMap = this.buildIndexMap();
62431 return this.indexMap.dataToCol[dataIndex];
62435 * Set a css style for a column dynamically.
62436 * @param {Number} colIndex The index of the column
62437 * @param {String} name The css property name
62438 * @param {String} value The css value
62440 setCSSStyle : function(colIndex, name, value){
62441 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
62442 Roo.util.CSS.updateRule(selector, name, value);
62445 generateRules : function(cm){
62446 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
62447 Roo.util.CSS.removeStyleSheet(rulesId);
62448 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62449 var cid = cm.getColumnId(i);
62450 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
62451 this.tdSelector, cid, " {\n}\n",
62452 this.hdSelector, cid, " {\n}\n",
62453 this.splitSelector, cid, " {\n}\n");
62455 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
62459 * Ext JS Library 1.1.1
62460 * Copyright(c) 2006-2007, Ext JS, LLC.
62462 * Originally Released Under LGPL - original licence link has changed is not relivant.
62465 * <script type="text/javascript">
62469 // This is a support class used internally by the Grid components
62470 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
62472 this.view = grid.getView();
62473 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62474 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
62476 this.setHandleElId(Roo.id(hd));
62477 this.setOuterHandleElId(Roo.id(hd2));
62479 this.scroll = false;
62481 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
62483 getDragData : function(e){
62484 var t = Roo.lib.Event.getTarget(e);
62485 var h = this.view.findHeaderCell(t);
62487 return {ddel: h.firstChild, header:h};
62492 onInitDrag : function(e){
62493 this.view.headersDisabled = true;
62494 var clone = this.dragData.ddel.cloneNode(true);
62495 clone.id = Roo.id();
62496 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
62497 this.proxy.update(clone);
62501 afterValidDrop : function(){
62503 setTimeout(function(){
62504 v.headersDisabled = false;
62508 afterInvalidDrop : function(){
62510 setTimeout(function(){
62511 v.headersDisabled = false;
62517 * Ext JS Library 1.1.1
62518 * Copyright(c) 2006-2007, Ext JS, LLC.
62520 * Originally Released Under LGPL - original licence link has changed is not relivant.
62523 * <script type="text/javascript">
62526 // This is a support class used internally by the Grid components
62527 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
62529 this.view = grid.getView();
62530 // split the proxies so they don't interfere with mouse events
62531 this.proxyTop = Roo.DomHelper.append(document.body, {
62532 cls:"col-move-top", html:" "
62534 this.proxyBottom = Roo.DomHelper.append(document.body, {
62535 cls:"col-move-bottom", html:" "
62537 this.proxyTop.hide = this.proxyBottom.hide = function(){
62538 this.setLeftTop(-100,-100);
62539 this.setStyle("visibility", "hidden");
62541 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62542 // temporarily disabled
62543 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
62544 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
62546 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
62547 proxyOffsets : [-4, -9],
62548 fly: Roo.Element.fly,
62550 getTargetFromEvent : function(e){
62551 var t = Roo.lib.Event.getTarget(e);
62552 var cindex = this.view.findCellIndex(t);
62553 if(cindex !== false){
62554 return this.view.getHeaderCell(cindex);
62559 nextVisible : function(h){
62560 var v = this.view, cm = this.grid.colModel;
62563 if(!cm.isHidden(v.getCellIndex(h))){
62571 prevVisible : function(h){
62572 var v = this.view, cm = this.grid.colModel;
62575 if(!cm.isHidden(v.getCellIndex(h))){
62583 positionIndicator : function(h, n, e){
62584 var x = Roo.lib.Event.getPageX(e);
62585 var r = Roo.lib.Dom.getRegion(n.firstChild);
62586 var px, pt, py = r.top + this.proxyOffsets[1];
62587 if((r.right - x) <= (r.right-r.left)/2){
62588 px = r.right+this.view.borderWidth;
62594 var oldIndex = this.view.getCellIndex(h);
62595 var newIndex = this.view.getCellIndex(n);
62597 if(this.grid.colModel.isFixed(newIndex)){
62601 var locked = this.grid.colModel.isLocked(newIndex);
62606 if(oldIndex < newIndex){
62609 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
62612 px += this.proxyOffsets[0];
62613 this.proxyTop.setLeftTop(px, py);
62614 this.proxyTop.show();
62615 if(!this.bottomOffset){
62616 this.bottomOffset = this.view.mainHd.getHeight();
62618 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
62619 this.proxyBottom.show();
62623 onNodeEnter : function(n, dd, e, data){
62624 if(data.header != n){
62625 this.positionIndicator(data.header, n, e);
62629 onNodeOver : function(n, dd, e, data){
62630 var result = false;
62631 if(data.header != n){
62632 result = this.positionIndicator(data.header, n, e);
62635 this.proxyTop.hide();
62636 this.proxyBottom.hide();
62638 return result ? this.dropAllowed : this.dropNotAllowed;
62641 onNodeOut : function(n, dd, e, data){
62642 this.proxyTop.hide();
62643 this.proxyBottom.hide();
62646 onNodeDrop : function(n, dd, e, data){
62647 var h = data.header;
62649 var cm = this.grid.colModel;
62650 var x = Roo.lib.Event.getPageX(e);
62651 var r = Roo.lib.Dom.getRegion(n.firstChild);
62652 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
62653 var oldIndex = this.view.getCellIndex(h);
62654 var newIndex = this.view.getCellIndex(n);
62655 var locked = cm.isLocked(newIndex);
62659 if(oldIndex < newIndex){
62662 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
62665 cm.setLocked(oldIndex, locked, true);
62666 cm.moveColumn(oldIndex, newIndex);
62667 this.grid.fireEvent("columnmove", oldIndex, newIndex);
62675 * Ext JS Library 1.1.1
62676 * Copyright(c) 2006-2007, Ext JS, LLC.
62678 * Originally Released Under LGPL - original licence link has changed is not relivant.
62681 * <script type="text/javascript">
62685 * @class Roo.grid.GridView
62686 * @extends Roo.util.Observable
62689 * @param {Object} config
62691 Roo.grid.GridView = function(config){
62692 Roo.grid.GridView.superclass.constructor.call(this);
62695 Roo.apply(this, config);
62698 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
62700 unselectable : 'unselectable="on"',
62701 unselectableCls : 'x-unselectable',
62704 rowClass : "x-grid-row",
62706 cellClass : "x-grid-col",
62708 tdClass : "x-grid-td",
62710 hdClass : "x-grid-hd",
62712 splitClass : "x-grid-split",
62714 sortClasses : ["sort-asc", "sort-desc"],
62716 enableMoveAnim : false,
62720 dh : Roo.DomHelper,
62722 fly : Roo.Element.fly,
62724 css : Roo.util.CSS,
62730 scrollIncrement : 22,
62732 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
62734 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
62736 bind : function(ds, cm){
62738 this.ds.un("load", this.onLoad, this);
62739 this.ds.un("datachanged", this.onDataChange, this);
62740 this.ds.un("add", this.onAdd, this);
62741 this.ds.un("remove", this.onRemove, this);
62742 this.ds.un("update", this.onUpdate, this);
62743 this.ds.un("clear", this.onClear, this);
62746 ds.on("load", this.onLoad, this);
62747 ds.on("datachanged", this.onDataChange, this);
62748 ds.on("add", this.onAdd, this);
62749 ds.on("remove", this.onRemove, this);
62750 ds.on("update", this.onUpdate, this);
62751 ds.on("clear", this.onClear, this);
62756 this.cm.un("widthchange", this.onColWidthChange, this);
62757 this.cm.un("headerchange", this.onHeaderChange, this);
62758 this.cm.un("hiddenchange", this.onHiddenChange, this);
62759 this.cm.un("columnmoved", this.onColumnMove, this);
62760 this.cm.un("columnlockchange", this.onColumnLock, this);
62763 this.generateRules(cm);
62764 cm.on("widthchange", this.onColWidthChange, this);
62765 cm.on("headerchange", this.onHeaderChange, this);
62766 cm.on("hiddenchange", this.onHiddenChange, this);
62767 cm.on("columnmoved", this.onColumnMove, this);
62768 cm.on("columnlockchange", this.onColumnLock, this);
62773 init: function(grid){
62774 Roo.grid.GridView.superclass.init.call(this, grid);
62776 this.bind(grid.dataSource, grid.colModel);
62778 grid.on("headerclick", this.handleHeaderClick, this);
62780 if(grid.trackMouseOver){
62781 grid.on("mouseover", this.onRowOver, this);
62782 grid.on("mouseout", this.onRowOut, this);
62784 grid.cancelTextSelection = function(){};
62785 this.gridId = grid.id;
62787 var tpls = this.templates || {};
62790 tpls.master = new Roo.Template(
62791 '<div class="x-grid" hidefocus="true">',
62792 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
62793 '<div class="x-grid-topbar"></div>',
62794 '<div class="x-grid-scroller"><div></div></div>',
62795 '<div class="x-grid-locked">',
62796 '<div class="x-grid-header">{lockedHeader}</div>',
62797 '<div class="x-grid-body">{lockedBody}</div>',
62799 '<div class="x-grid-viewport">',
62800 '<div class="x-grid-header">{header}</div>',
62801 '<div class="x-grid-body">{body}</div>',
62803 '<div class="x-grid-bottombar"></div>',
62805 '<div class="x-grid-resize-proxy"> </div>',
62808 tpls.master.disableformats = true;
62812 tpls.header = new Roo.Template(
62813 '<table border="0" cellspacing="0" cellpadding="0">',
62814 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
62817 tpls.header.disableformats = true;
62819 tpls.header.compile();
62822 tpls.hcell = new Roo.Template(
62823 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
62824 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
62827 tpls.hcell.disableFormats = true;
62829 tpls.hcell.compile();
62832 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
62833 this.unselectableCls + '" ' + this.unselectable +'> </div>');
62834 tpls.hsplit.disableFormats = true;
62836 tpls.hsplit.compile();
62839 tpls.body = new Roo.Template(
62840 '<table border="0" cellspacing="0" cellpadding="0">',
62841 "<tbody>{rows}</tbody>",
62844 tpls.body.disableFormats = true;
62846 tpls.body.compile();
62849 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
62850 tpls.row.disableFormats = true;
62852 tpls.row.compile();
62855 tpls.cell = new Roo.Template(
62856 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
62857 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
62858 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
62861 tpls.cell.disableFormats = true;
62863 tpls.cell.compile();
62865 this.templates = tpls;
62868 // remap these for backwards compat
62869 onColWidthChange : function(){
62870 this.updateColumns.apply(this, arguments);
62872 onHeaderChange : function(){
62873 this.updateHeaders.apply(this, arguments);
62875 onHiddenChange : function(){
62876 this.handleHiddenChange.apply(this, arguments);
62878 onColumnMove : function(){
62879 this.handleColumnMove.apply(this, arguments);
62881 onColumnLock : function(){
62882 this.handleLockChange.apply(this, arguments);
62885 onDataChange : function(){
62887 this.updateHeaderSortState();
62890 onClear : function(){
62894 onUpdate : function(ds, record){
62895 this.refreshRow(record);
62898 refreshRow : function(record){
62899 var ds = this.ds, index;
62900 if(typeof record == 'number'){
62902 record = ds.getAt(index);
62904 index = ds.indexOf(record);
62906 this.insertRows(ds, index, index, true);
62907 this.onRemove(ds, record, index+1, true);
62908 this.syncRowHeights(index, index);
62910 this.fireEvent("rowupdated", this, index, record);
62913 onAdd : function(ds, records, index){
62914 this.insertRows(ds, index, index + (records.length-1));
62917 onRemove : function(ds, record, index, isUpdate){
62918 if(isUpdate !== true){
62919 this.fireEvent("beforerowremoved", this, index, record);
62921 var bt = this.getBodyTable(), lt = this.getLockedTable();
62922 if(bt.rows[index]){
62923 bt.firstChild.removeChild(bt.rows[index]);
62925 if(lt.rows[index]){
62926 lt.firstChild.removeChild(lt.rows[index]);
62928 if(isUpdate !== true){
62929 this.stripeRows(index);
62930 this.syncRowHeights(index, index);
62932 this.fireEvent("rowremoved", this, index, record);
62936 onLoad : function(){
62937 this.scrollToTop();
62941 * Scrolls the grid to the top
62943 scrollToTop : function(){
62945 this.scroller.dom.scrollTop = 0;
62951 * Gets a panel in the header of the grid that can be used for toolbars etc.
62952 * After modifying the contents of this panel a call to grid.autoSize() may be
62953 * required to register any changes in size.
62954 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
62955 * @return Roo.Element
62957 getHeaderPanel : function(doShow){
62959 this.headerPanel.show();
62961 return this.headerPanel;
62965 * Gets a panel in the footer of the grid that can be used for toolbars etc.
62966 * After modifying the contents of this panel a call to grid.autoSize() may be
62967 * required to register any changes in size.
62968 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
62969 * @return Roo.Element
62971 getFooterPanel : function(doShow){
62973 this.footerPanel.show();
62975 return this.footerPanel;
62978 initElements : function(){
62979 var E = Roo.Element;
62980 var el = this.grid.getGridEl().dom.firstChild;
62981 var cs = el.childNodes;
62983 this.el = new E(el);
62985 this.focusEl = new E(el.firstChild);
62986 this.focusEl.swallowEvent("click", true);
62988 this.headerPanel = new E(cs[1]);
62989 this.headerPanel.enableDisplayMode("block");
62991 this.scroller = new E(cs[2]);
62992 this.scrollSizer = new E(this.scroller.dom.firstChild);
62994 this.lockedWrap = new E(cs[3]);
62995 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
62996 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
62998 this.mainWrap = new E(cs[4]);
62999 this.mainHd = new E(this.mainWrap.dom.firstChild);
63000 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
63002 this.footerPanel = new E(cs[5]);
63003 this.footerPanel.enableDisplayMode("block");
63005 this.resizeProxy = new E(cs[6]);
63007 this.headerSelector = String.format(
63008 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
63009 this.lockedHd.id, this.mainHd.id
63012 this.splitterSelector = String.format(
63013 '#{0} div.x-grid-split, #{1} div.x-grid-split',
63014 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
63017 idToCssName : function(s)
63019 return s.replace(/[^a-z0-9]+/ig, '-');
63022 getHeaderCell : function(index){
63023 return Roo.DomQuery.select(this.headerSelector)[index];
63026 getHeaderCellMeasure : function(index){
63027 return this.getHeaderCell(index).firstChild;
63030 getHeaderCellText : function(index){
63031 return this.getHeaderCell(index).firstChild.firstChild;
63034 getLockedTable : function(){
63035 return this.lockedBody.dom.firstChild;
63038 getBodyTable : function(){
63039 return this.mainBody.dom.firstChild;
63042 getLockedRow : function(index){
63043 return this.getLockedTable().rows[index];
63046 getRow : function(index){
63047 return this.getBodyTable().rows[index];
63050 getRowComposite : function(index){
63052 this.rowEl = new Roo.CompositeElementLite();
63054 var els = [], lrow, mrow;
63055 if(lrow = this.getLockedRow(index)){
63058 if(mrow = this.getRow(index)){
63061 this.rowEl.elements = els;
63065 * Gets the 'td' of the cell
63067 * @param {Integer} rowIndex row to select
63068 * @param {Integer} colIndex column to select
63072 getCell : function(rowIndex, colIndex){
63073 var locked = this.cm.getLockedCount();
63075 if(colIndex < locked){
63076 source = this.lockedBody.dom.firstChild;
63078 source = this.mainBody.dom.firstChild;
63079 colIndex -= locked;
63081 return source.rows[rowIndex].childNodes[colIndex];
63084 getCellText : function(rowIndex, colIndex){
63085 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
63088 getCellBox : function(cell){
63089 var b = this.fly(cell).getBox();
63090 if(Roo.isOpera){ // opera fails to report the Y
63091 b.y = cell.offsetTop + this.mainBody.getY();
63096 getCellIndex : function(cell){
63097 var id = String(cell.className).match(this.cellRE);
63099 return parseInt(id[1], 10);
63104 findHeaderIndex : function(n){
63105 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
63106 return r ? this.getCellIndex(r) : false;
63109 findHeaderCell : function(n){
63110 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
63111 return r ? r : false;
63114 findRowIndex : function(n){
63118 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
63119 return r ? r.rowIndex : false;
63122 findCellIndex : function(node){
63123 var stop = this.el.dom;
63124 while(node && node != stop){
63125 if(this.findRE.test(node.className)){
63126 return this.getCellIndex(node);
63128 node = node.parentNode;
63133 getColumnId : function(index){
63134 return this.cm.getColumnId(index);
63137 getSplitters : function()
63139 if(this.splitterSelector){
63140 return Roo.DomQuery.select(this.splitterSelector);
63146 getSplitter : function(index){
63147 return this.getSplitters()[index];
63150 onRowOver : function(e, t){
63152 if((row = this.findRowIndex(t)) !== false){
63153 this.getRowComposite(row).addClass("x-grid-row-over");
63157 onRowOut : function(e, t){
63159 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
63160 this.getRowComposite(row).removeClass("x-grid-row-over");
63164 renderHeaders : function(){
63166 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
63167 var cb = [], lb = [], sb = [], lsb = [], p = {};
63168 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63169 p.cellId = "x-grid-hd-0-" + i;
63170 p.splitId = "x-grid-csplit-0-" + i;
63171 p.id = cm.getColumnId(i);
63172 p.value = cm.getColumnHeader(i) || "";
63173 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
63174 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
63175 if(!cm.isLocked(i)){
63176 cb[cb.length] = ct.apply(p);
63177 sb[sb.length] = st.apply(p);
63179 lb[lb.length] = ct.apply(p);
63180 lsb[lsb.length] = st.apply(p);
63183 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
63184 ht.apply({cells: cb.join(""), splits:sb.join("")})];
63187 updateHeaders : function(){
63188 var html = this.renderHeaders();
63189 this.lockedHd.update(html[0]);
63190 this.mainHd.update(html[1]);
63194 * Focuses the specified row.
63195 * @param {Number} row The row index
63197 focusRow : function(row)
63199 //Roo.log('GridView.focusRow');
63200 var x = this.scroller.dom.scrollLeft;
63201 this.focusCell(row, 0, false);
63202 this.scroller.dom.scrollLeft = x;
63206 * Focuses the specified cell.
63207 * @param {Number} row The row index
63208 * @param {Number} col The column index
63209 * @param {Boolean} hscroll false to disable horizontal scrolling
63211 focusCell : function(row, col, hscroll)
63213 //Roo.log('GridView.focusCell');
63214 var el = this.ensureVisible(row, col, hscroll);
63215 this.focusEl.alignTo(el, "tl-tl");
63217 this.focusEl.focus();
63219 this.focusEl.focus.defer(1, this.focusEl);
63224 * Scrolls the specified cell into view
63225 * @param {Number} row The row index
63226 * @param {Number} col The column index
63227 * @param {Boolean} hscroll false to disable horizontal scrolling
63229 ensureVisible : function(row, col, hscroll)
63231 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
63232 //return null; //disable for testing.
63233 if(typeof row != "number"){
63234 row = row.rowIndex;
63236 if(row < 0 && row >= this.ds.getCount()){
63239 col = (col !== undefined ? col : 0);
63240 var cm = this.grid.colModel;
63241 while(cm.isHidden(col)){
63245 var el = this.getCell(row, col);
63249 var c = this.scroller.dom;
63251 var ctop = parseInt(el.offsetTop, 10);
63252 var cleft = parseInt(el.offsetLeft, 10);
63253 var cbot = ctop + el.offsetHeight;
63254 var cright = cleft + el.offsetWidth;
63256 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
63257 var stop = parseInt(c.scrollTop, 10);
63258 var sleft = parseInt(c.scrollLeft, 10);
63259 var sbot = stop + ch;
63260 var sright = sleft + c.clientWidth;
63262 Roo.log('GridView.ensureVisible:' +
63264 ' c.clientHeight:' + c.clientHeight +
63265 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
63273 c.scrollTop = ctop;
63274 //Roo.log("set scrolltop to ctop DISABLE?");
63275 }else if(cbot > sbot){
63276 //Roo.log("set scrolltop to cbot-ch");
63277 c.scrollTop = cbot-ch;
63280 if(hscroll !== false){
63282 c.scrollLeft = cleft;
63283 }else if(cright > sright){
63284 c.scrollLeft = cright-c.clientWidth;
63291 updateColumns : function(){
63292 this.grid.stopEditing();
63293 var cm = this.grid.colModel, colIds = this.getColumnIds();
63294 //var totalWidth = cm.getTotalWidth();
63296 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63297 //if(cm.isHidden(i)) continue;
63298 var w = cm.getColumnWidth(i);
63299 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63300 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63302 this.updateSplitters();
63305 generateRules : function(cm){
63306 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
63307 Roo.util.CSS.removeStyleSheet(rulesId);
63308 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63309 var cid = cm.getColumnId(i);
63311 if(cm.config[i].align){
63312 align = 'text-align:'+cm.config[i].align+';';
63315 if(cm.isHidden(i)){
63316 hidden = 'display:none;';
63318 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
63320 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
63321 this.hdSelector, cid, " {\n", align, width, "}\n",
63322 this.tdSelector, cid, " {\n",hidden,"\n}\n",
63323 this.splitSelector, cid, " {\n", hidden , "\n}\n");
63325 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
63328 updateSplitters : function(){
63329 var cm = this.cm, s = this.getSplitters();
63330 if(s){ // splitters not created yet
63331 var pos = 0, locked = true;
63332 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63333 if(cm.isHidden(i)) {
63336 var w = cm.getColumnWidth(i); // make sure it's a number
63337 if(!cm.isLocked(i) && locked){
63342 s[i].style.left = (pos-this.splitOffset) + "px";
63347 handleHiddenChange : function(colModel, colIndex, hidden){
63349 this.hideColumn(colIndex);
63351 this.unhideColumn(colIndex);
63355 hideColumn : function(colIndex){
63356 var cid = this.getColumnId(colIndex);
63357 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
63358 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
63360 this.updateHeaders();
63362 this.updateSplitters();
63366 unhideColumn : function(colIndex){
63367 var cid = this.getColumnId(colIndex);
63368 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
63369 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
63372 this.updateHeaders();
63374 this.updateSplitters();
63378 insertRows : function(dm, firstRow, lastRow, isUpdate){
63379 if(firstRow == 0 && lastRow == dm.getCount()-1){
63383 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
63385 var s = this.getScrollState();
63386 var markup = this.renderRows(firstRow, lastRow);
63387 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
63388 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
63389 this.restoreScroll(s);
63391 this.fireEvent("rowsinserted", this, firstRow, lastRow);
63392 this.syncRowHeights(firstRow, lastRow);
63393 this.stripeRows(firstRow);
63399 bufferRows : function(markup, target, index){
63400 var before = null, trows = target.rows, tbody = target.tBodies[0];
63401 if(index < trows.length){
63402 before = trows[index];
63404 var b = document.createElement("div");
63405 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
63406 var rows = b.firstChild.rows;
63407 for(var i = 0, len = rows.length; i < len; i++){
63409 tbody.insertBefore(rows[0], before);
63411 tbody.appendChild(rows[0]);
63418 deleteRows : function(dm, firstRow, lastRow){
63419 if(dm.getRowCount()<1){
63420 this.fireEvent("beforerefresh", this);
63421 this.mainBody.update("");
63422 this.lockedBody.update("");
63423 this.fireEvent("refresh", this);
63425 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
63426 var bt = this.getBodyTable();
63427 var tbody = bt.firstChild;
63428 var rows = bt.rows;
63429 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
63430 tbody.removeChild(rows[firstRow]);
63432 this.stripeRows(firstRow);
63433 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
63437 updateRows : function(dataSource, firstRow, lastRow){
63438 var s = this.getScrollState();
63440 this.restoreScroll(s);
63443 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
63447 this.updateHeaderSortState();
63450 getScrollState : function(){
63452 var sb = this.scroller.dom;
63453 return {left: sb.scrollLeft, top: sb.scrollTop};
63456 stripeRows : function(startRow){
63457 if(!this.grid.stripeRows || this.ds.getCount() < 1){
63460 startRow = startRow || 0;
63461 var rows = this.getBodyTable().rows;
63462 var lrows = this.getLockedTable().rows;
63463 var cls = ' x-grid-row-alt ';
63464 for(var i = startRow, len = rows.length; i < len; i++){
63465 var row = rows[i], lrow = lrows[i];
63466 var isAlt = ((i+1) % 2 == 0);
63467 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
63468 if(isAlt == hasAlt){
63472 row.className += " x-grid-row-alt";
63474 row.className = row.className.replace("x-grid-row-alt", "");
63477 lrow.className = row.className;
63482 restoreScroll : function(state){
63483 //Roo.log('GridView.restoreScroll');
63484 var sb = this.scroller.dom;
63485 sb.scrollLeft = state.left;
63486 sb.scrollTop = state.top;
63490 syncScroll : function(){
63491 //Roo.log('GridView.syncScroll');
63492 var sb = this.scroller.dom;
63493 var sh = this.mainHd.dom;
63494 var bs = this.mainBody.dom;
63495 var lv = this.lockedBody.dom;
63496 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
63497 lv.scrollTop = bs.scrollTop = sb.scrollTop;
63500 handleScroll : function(e){
63502 var sb = this.scroller.dom;
63503 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
63507 handleWheel : function(e){
63508 var d = e.getWheelDelta();
63509 this.scroller.dom.scrollTop -= d*22;
63510 // set this here to prevent jumpy scrolling on large tables
63511 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
63515 renderRows : function(startRow, endRow){
63516 // pull in all the crap needed to render rows
63517 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
63518 var colCount = cm.getColumnCount();
63520 if(ds.getCount() < 1){
63524 // build a map for all the columns
63526 for(var i = 0; i < colCount; i++){
63527 var name = cm.getDataIndex(i);
63529 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
63530 renderer : cm.getRenderer(i),
63531 id : cm.getColumnId(i),
63532 locked : cm.isLocked(i),
63533 has_editor : cm.isCellEditable(i)
63537 startRow = startRow || 0;
63538 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
63540 // records to render
63541 var rs = ds.getRange(startRow, endRow);
63543 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
63546 // As much as I hate to duplicate code, this was branched because FireFox really hates
63547 // [].join("") on strings. The performance difference was substantial enough to
63548 // branch this function
63549 doRender : Roo.isGecko ?
63550 function(cs, rs, ds, startRow, colCount, stripe){
63551 var ts = this.templates, ct = ts.cell, rt = ts.row;
63553 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63555 var hasListener = this.grid.hasListener('rowclass');
63557 for(var j = 0, len = rs.length; j < len; j++){
63558 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
63559 for(var i = 0; i < colCount; i++){
63561 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63563 p.css = p.attr = "";
63564 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63565 if(p.value == undefined || p.value === "") {
63566 p.value = " ";
63569 p.css += ' x-grid-editable-cell';
63571 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
63572 p.css += ' x-grid-dirty-cell';
63574 var markup = ct.apply(p);
63582 if(stripe && ((rowIndex+1) % 2 == 0)){
63583 alt.push("x-grid-row-alt")
63586 alt.push( " x-grid-dirty-row");
63589 if(this.getRowClass){
63590 alt.push(this.getRowClass(r, rowIndex));
63596 rowIndex : rowIndex,
63599 this.grid.fireEvent('rowclass', this, rowcfg);
63600 alt.push(rowcfg.rowClass);
63602 rp.alt = alt.join(" ");
63603 lbuf+= rt.apply(rp);
63605 buf+= rt.apply(rp);
63607 return [lbuf, buf];
63609 function(cs, rs, ds, startRow, colCount, stripe){
63610 var ts = this.templates, ct = ts.cell, rt = ts.row;
63612 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63613 var hasListener = this.grid.hasListener('rowclass');
63616 for(var j = 0, len = rs.length; j < len; j++){
63617 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
63618 for(var i = 0; i < colCount; i++){
63620 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63622 p.css = p.attr = "";
63623 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63624 if(p.value == undefined || p.value === "") {
63625 p.value = " ";
63629 p.css += ' x-grid-editable-cell';
63631 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
63632 p.css += ' x-grid-dirty-cell'
63635 var markup = ct.apply(p);
63637 cb[cb.length] = markup;
63639 lcb[lcb.length] = markup;
63643 if(stripe && ((rowIndex+1) % 2 == 0)){
63644 alt.push( "x-grid-row-alt");
63647 alt.push(" x-grid-dirty-row");
63650 if(this.getRowClass){
63651 alt.push( this.getRowClass(r, rowIndex));
63657 rowIndex : rowIndex,
63660 this.grid.fireEvent('rowclass', this, rowcfg);
63661 alt.push(rowcfg.rowClass);
63664 rp.alt = alt.join(" ");
63665 rp.cells = lcb.join("");
63666 lbuf[lbuf.length] = rt.apply(rp);
63667 rp.cells = cb.join("");
63668 buf[buf.length] = rt.apply(rp);
63670 return [lbuf.join(""), buf.join("")];
63673 renderBody : function(){
63674 var markup = this.renderRows();
63675 var bt = this.templates.body;
63676 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
63680 * Refreshes the grid
63681 * @param {Boolean} headersToo
63683 refresh : function(headersToo){
63684 this.fireEvent("beforerefresh", this);
63685 this.grid.stopEditing();
63686 var result = this.renderBody();
63687 this.lockedBody.update(result[0]);
63688 this.mainBody.update(result[1]);
63689 if(headersToo === true){
63690 this.updateHeaders();
63691 this.updateColumns();
63692 this.updateSplitters();
63693 this.updateHeaderSortState();
63695 this.syncRowHeights();
63697 this.fireEvent("refresh", this);
63700 handleColumnMove : function(cm, oldIndex, newIndex){
63701 this.indexMap = null;
63702 var s = this.getScrollState();
63703 this.refresh(true);
63704 this.restoreScroll(s);
63705 this.afterMove(newIndex);
63708 afterMove : function(colIndex){
63709 if(this.enableMoveAnim && Roo.enableFx){
63710 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
63712 // if multisort - fix sortOrder, and reload..
63713 if (this.grid.dataSource.multiSort) {
63714 // the we can call sort again..
63715 var dm = this.grid.dataSource;
63716 var cm = this.grid.colModel;
63718 for(var i = 0; i < cm.config.length; i++ ) {
63720 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
63721 continue; // dont' bother, it's not in sort list or being set.
63724 so.push(cm.config[i].dataIndex);
63727 dm.load(dm.lastOptions);
63734 updateCell : function(dm, rowIndex, dataIndex){
63735 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
63736 if(typeof colIndex == "undefined"){ // not present in grid
63739 var cm = this.grid.colModel;
63740 var cell = this.getCell(rowIndex, colIndex);
63741 var cellText = this.getCellText(rowIndex, colIndex);
63744 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
63745 id : cm.getColumnId(colIndex),
63746 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
63748 var renderer = cm.getRenderer(colIndex);
63749 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
63750 if(typeof val == "undefined" || val === "") {
63753 cellText.innerHTML = val;
63754 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
63755 this.syncRowHeights(rowIndex, rowIndex);
63758 calcColumnWidth : function(colIndex, maxRowsToMeasure){
63760 if(this.grid.autoSizeHeaders){
63761 var h = this.getHeaderCellMeasure(colIndex);
63762 maxWidth = Math.max(maxWidth, h.scrollWidth);
63765 if(this.cm.isLocked(colIndex)){
63766 tb = this.getLockedTable();
63769 tb = this.getBodyTable();
63770 index = colIndex - this.cm.getLockedCount();
63773 var rows = tb.rows;
63774 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
63775 for(var i = 0; i < stopIndex; i++){
63776 var cell = rows[i].childNodes[index].firstChild;
63777 maxWidth = Math.max(maxWidth, cell.scrollWidth);
63780 return maxWidth + /*margin for error in IE*/ 5;
63783 * Autofit a column to its content.
63784 * @param {Number} colIndex
63785 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
63787 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
63788 if(this.cm.isHidden(colIndex)){
63789 return; // can't calc a hidden column
63792 var cid = this.cm.getColumnId(colIndex);
63793 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
63794 if(this.grid.autoSizeHeaders){
63795 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
63798 var newWidth = this.calcColumnWidth(colIndex);
63799 this.cm.setColumnWidth(colIndex,
63800 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
63801 if(!suppressEvent){
63802 this.grid.fireEvent("columnresize", colIndex, newWidth);
63807 * Autofits all columns to their content and then expands to fit any extra space in the grid
63809 autoSizeColumns : function(){
63810 var cm = this.grid.colModel;
63811 var colCount = cm.getColumnCount();
63812 for(var i = 0; i < colCount; i++){
63813 this.autoSizeColumn(i, true, true);
63815 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
63818 this.updateColumns();
63824 * Autofits all columns to the grid's width proportionate with their current size
63825 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
63827 fitColumns : function(reserveScrollSpace){
63828 var cm = this.grid.colModel;
63829 var colCount = cm.getColumnCount();
63833 for (i = 0; i < colCount; i++){
63834 if(!cm.isHidden(i) && !cm.isFixed(i)){
63835 w = cm.getColumnWidth(i);
63841 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
63842 if(reserveScrollSpace){
63845 var frac = (avail - cm.getTotalWidth())/width;
63846 while (cols.length){
63849 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
63851 this.updateColumns();
63855 onRowSelect : function(rowIndex){
63856 var row = this.getRowComposite(rowIndex);
63857 row.addClass("x-grid-row-selected");
63860 onRowDeselect : function(rowIndex){
63861 var row = this.getRowComposite(rowIndex);
63862 row.removeClass("x-grid-row-selected");
63865 onCellSelect : function(row, col){
63866 var cell = this.getCell(row, col);
63868 Roo.fly(cell).addClass("x-grid-cell-selected");
63872 onCellDeselect : function(row, col){
63873 var cell = this.getCell(row, col);
63875 Roo.fly(cell).removeClass("x-grid-cell-selected");
63879 updateHeaderSortState : function(){
63881 // sort state can be single { field: xxx, direction : yyy}
63882 // or { xxx=>ASC , yyy : DESC ..... }
63885 if (!this.ds.multiSort) {
63886 var state = this.ds.getSortState();
63890 mstate[state.field] = state.direction;
63891 // FIXME... - this is not used here.. but might be elsewhere..
63892 this.sortState = state;
63895 mstate = this.ds.sortToggle;
63897 //remove existing sort classes..
63899 var sc = this.sortClasses;
63900 var hds = this.el.select(this.headerSelector).removeClass(sc);
63902 for(var f in mstate) {
63904 var sortColumn = this.cm.findColumnIndex(f);
63906 if(sortColumn != -1){
63907 var sortDir = mstate[f];
63908 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
63917 handleHeaderClick : function(g, index,e){
63919 Roo.log("header click");
63922 // touch events on header are handled by context
63923 this.handleHdCtx(g,index,e);
63928 if(this.headersDisabled){
63931 var dm = g.dataSource, cm = g.colModel;
63932 if(!cm.isSortable(index)){
63937 if (dm.multiSort) {
63938 // update the sortOrder
63940 for(var i = 0; i < cm.config.length; i++ ) {
63942 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
63943 continue; // dont' bother, it's not in sort list or being set.
63946 so.push(cm.config[i].dataIndex);
63952 dm.sort(cm.getDataIndex(index));
63956 destroy : function(){
63958 this.colMenu.removeAll();
63959 Roo.menu.MenuMgr.unregister(this.colMenu);
63960 this.colMenu.getEl().remove();
63961 delete this.colMenu;
63964 this.hmenu.removeAll();
63965 Roo.menu.MenuMgr.unregister(this.hmenu);
63966 this.hmenu.getEl().remove();
63969 if(this.grid.enableColumnMove){
63970 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63972 for(var dd in dds){
63973 if(!dds[dd].config.isTarget && dds[dd].dragElId){
63974 var elid = dds[dd].dragElId;
63976 Roo.get(elid).remove();
63977 } else if(dds[dd].config.isTarget){
63978 dds[dd].proxyTop.remove();
63979 dds[dd].proxyBottom.remove();
63982 if(Roo.dd.DDM.locationCache[dd]){
63983 delete Roo.dd.DDM.locationCache[dd];
63986 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63989 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
63990 this.bind(null, null);
63991 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
63994 handleLockChange : function(){
63995 this.refresh(true);
63998 onDenyColumnLock : function(){
64002 onDenyColumnHide : function(){
64006 handleHdMenuClick : function(item){
64007 var index = this.hdCtxIndex;
64008 var cm = this.cm, ds = this.ds;
64011 ds.sort(cm.getDataIndex(index), "ASC");
64014 ds.sort(cm.getDataIndex(index), "DESC");
64017 var lc = cm.getLockedCount();
64018 if(cm.getColumnCount(true) <= lc+1){
64019 this.onDenyColumnLock();
64023 cm.setLocked(index, true, true);
64024 cm.moveColumn(index, lc);
64025 this.grid.fireEvent("columnmove", index, lc);
64027 cm.setLocked(index, true);
64031 var lc = cm.getLockedCount();
64032 if((lc-1) != index){
64033 cm.setLocked(index, false, true);
64034 cm.moveColumn(index, lc-1);
64035 this.grid.fireEvent("columnmove", index, lc-1);
64037 cm.setLocked(index, false);
64040 case 'wider': // used to expand cols on touch..
64042 var cw = cm.getColumnWidth(index);
64043 cw += (item.id == 'wider' ? 1 : -1) * 50;
64044 cw = Math.max(0, cw);
64045 cw = Math.min(cw,4000);
64046 cm.setColumnWidth(index, cw);
64050 index = cm.getIndexById(item.id.substr(4));
64052 if(item.checked && cm.getColumnCount(true) <= 1){
64053 this.onDenyColumnHide();
64056 cm.setHidden(index, item.checked);
64062 beforeColMenuShow : function(){
64063 var cm = this.cm, colCount = cm.getColumnCount();
64064 this.colMenu.removeAll();
64067 for(var i = 0; i < colCount; i++){
64069 id: "col-"+cm.getColumnId(i),
64070 text: cm.getColumnHeader(i),
64071 checked: !cm.isHidden(i),
64076 if (this.grid.sortColMenu) {
64077 items.sort(function(a,b) {
64078 if (a.text == b.text) {
64081 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
64085 for(var i = 0; i < colCount; i++){
64086 this.colMenu.add(new Roo.menu.CheckItem(items[i]));
64090 handleHdCtx : function(g, index, e){
64092 var hd = this.getHeaderCell(index);
64093 this.hdCtxIndex = index;
64094 var ms = this.hmenu.items, cm = this.cm;
64095 ms.get("asc").setDisabled(!cm.isSortable(index));
64096 ms.get("desc").setDisabled(!cm.isSortable(index));
64097 if(this.grid.enableColLock !== false){
64098 ms.get("lock").setDisabled(cm.isLocked(index));
64099 ms.get("unlock").setDisabled(!cm.isLocked(index));
64101 this.hmenu.show(hd, "tl-bl");
64104 handleHdOver : function(e){
64105 var hd = this.findHeaderCell(e.getTarget());
64106 if(hd && !this.headersDisabled){
64107 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
64108 this.fly(hd).addClass("x-grid-hd-over");
64113 handleHdOut : function(e){
64114 var hd = this.findHeaderCell(e.getTarget());
64116 this.fly(hd).removeClass("x-grid-hd-over");
64120 handleSplitDblClick : function(e, t){
64121 var i = this.getCellIndex(t);
64122 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
64123 this.autoSizeColumn(i, true);
64128 render : function(){
64131 var colCount = cm.getColumnCount();
64133 if(this.grid.monitorWindowResize === true){
64134 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
64136 var header = this.renderHeaders();
64137 var body = this.templates.body.apply({rows:""});
64138 var html = this.templates.master.apply({
64141 lockedHeader: header[0],
64145 //this.updateColumns();
64147 this.grid.getGridEl().dom.innerHTML = html;
64149 this.initElements();
64151 // a kludge to fix the random scolling effect in webkit
64152 this.el.on("scroll", function() {
64153 this.el.dom.scrollTop=0; // hopefully not recursive..
64156 this.scroller.on("scroll", this.handleScroll, this);
64157 this.lockedBody.on("mousewheel", this.handleWheel, this);
64158 this.mainBody.on("mousewheel", this.handleWheel, this);
64160 this.mainHd.on("mouseover", this.handleHdOver, this);
64161 this.mainHd.on("mouseout", this.handleHdOut, this);
64162 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
64163 {delegate: "."+this.splitClass});
64165 this.lockedHd.on("mouseover", this.handleHdOver, this);
64166 this.lockedHd.on("mouseout", this.handleHdOut, this);
64167 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
64168 {delegate: "."+this.splitClass});
64170 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
64171 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64174 this.updateSplitters();
64176 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
64177 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64178 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64181 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
64182 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
64184 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
64185 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
64187 if(this.grid.enableColLock !== false){
64188 this.hmenu.add('-',
64189 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
64190 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
64194 this.hmenu.add('-',
64195 {id:"wider", text: this.columnsWiderText},
64196 {id:"narrow", text: this.columnsNarrowText }
64202 if(this.grid.enableColumnHide !== false){
64204 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
64205 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
64206 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
64208 this.hmenu.add('-',
64209 {id:"columns", text: this.columnsText, menu: this.colMenu}
64212 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
64214 this.grid.on("headercontextmenu", this.handleHdCtx, this);
64217 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
64218 this.dd = new Roo.grid.GridDragZone(this.grid, {
64219 ddGroup : this.grid.ddGroup || 'GridDD'
64225 for(var i = 0; i < colCount; i++){
64226 if(cm.isHidden(i)){
64227 this.hideColumn(i);
64229 if(cm.config[i].align){
64230 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
64231 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
64235 this.updateHeaderSortState();
64237 this.beforeInitialResize();
64240 // two part rendering gives faster view to the user
64241 this.renderPhase2.defer(1, this);
64244 renderPhase2 : function(){
64245 // render the rows now
64247 if(this.grid.autoSizeColumns){
64248 this.autoSizeColumns();
64252 beforeInitialResize : function(){
64256 onColumnSplitterMoved : function(i, w){
64257 this.userResized = true;
64258 var cm = this.grid.colModel;
64259 cm.setColumnWidth(i, w, true);
64260 var cid = cm.getColumnId(i);
64261 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
64262 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
64263 this.updateSplitters();
64265 this.grid.fireEvent("columnresize", i, w);
64268 syncRowHeights : function(startIndex, endIndex){
64269 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
64270 startIndex = startIndex || 0;
64271 var mrows = this.getBodyTable().rows;
64272 var lrows = this.getLockedTable().rows;
64273 var len = mrows.length-1;
64274 endIndex = Math.min(endIndex || len, len);
64275 for(var i = startIndex; i <= endIndex; i++){
64276 var m = mrows[i], l = lrows[i];
64277 var h = Math.max(m.offsetHeight, l.offsetHeight);
64278 m.style.height = l.style.height = h + "px";
64283 layout : function(initialRender, is2ndPass)
64286 var auto = g.autoHeight;
64287 var scrollOffset = 16;
64288 var c = g.getGridEl(), cm = this.cm,
64289 expandCol = g.autoExpandColumn,
64291 //c.beginMeasure();
64293 if(!c.dom.offsetWidth){ // display:none?
64295 this.lockedWrap.show();
64296 this.mainWrap.show();
64301 var hasLock = this.cm.isLocked(0);
64303 var tbh = this.headerPanel.getHeight();
64304 var bbh = this.footerPanel.getHeight();
64307 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
64308 var newHeight = ch + c.getBorderWidth("tb");
64310 newHeight = Math.min(g.maxHeight, newHeight);
64312 c.setHeight(newHeight);
64316 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
64319 var s = this.scroller;
64321 var csize = c.getSize(true);
64323 this.el.setSize(csize.width, csize.height);
64325 this.headerPanel.setWidth(csize.width);
64326 this.footerPanel.setWidth(csize.width);
64328 var hdHeight = this.mainHd.getHeight();
64329 var vw = csize.width;
64330 var vh = csize.height - (tbh + bbh);
64334 var bt = this.getBodyTable();
64336 if(cm.getLockedCount() == cm.config.length){
64337 bt = this.getLockedTable();
64340 var ltWidth = hasLock ?
64341 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
64343 var scrollHeight = bt.offsetHeight;
64344 var scrollWidth = ltWidth + bt.offsetWidth;
64345 var vscroll = false, hscroll = false;
64347 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
64349 var lw = this.lockedWrap, mw = this.mainWrap;
64350 var lb = this.lockedBody, mb = this.mainBody;
64352 setTimeout(function(){
64353 var t = s.dom.offsetTop;
64354 var w = s.dom.clientWidth,
64355 h = s.dom.clientHeight;
64358 lw.setSize(ltWidth, h);
64360 mw.setLeftTop(ltWidth, t);
64361 mw.setSize(w-ltWidth, h);
64363 lb.setHeight(h-hdHeight);
64364 mb.setHeight(h-hdHeight);
64366 if(is2ndPass !== true && !gv.userResized && expandCol){
64367 // high speed resize without full column calculation
64369 var ci = cm.getIndexById(expandCol);
64371 ci = cm.findColumnIndex(expandCol);
64373 ci = Math.max(0, ci); // make sure it's got at least the first col.
64374 var expandId = cm.getColumnId(ci);
64375 var tw = cm.getTotalWidth(false);
64376 var currentWidth = cm.getColumnWidth(ci);
64377 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
64378 if(currentWidth != cw){
64379 cm.setColumnWidth(ci, cw, true);
64380 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64381 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64382 gv.updateSplitters();
64383 gv.layout(false, true);
64395 onWindowResize : function(){
64396 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
64402 appendFooter : function(parentEl){
64406 sortAscText : "Sort Ascending",
64407 sortDescText : "Sort Descending",
64408 lockText : "Lock Column",
64409 unlockText : "Unlock Column",
64410 columnsText : "Columns",
64412 columnsWiderText : "Wider",
64413 columnsNarrowText : "Thinner"
64417 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
64418 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
64419 this.proxy.el.addClass('x-grid3-col-dd');
64422 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
64423 handleMouseDown : function(e){
64427 callHandleMouseDown : function(e){
64428 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
64433 * Ext JS Library 1.1.1
64434 * Copyright(c) 2006-2007, Ext JS, LLC.
64436 * Originally Released Under LGPL - original licence link has changed is not relivant.
64439 * <script type="text/javascript">
64442 * @extends Roo.dd.DDProxy
64443 * @class Roo.grid.SplitDragZone
64444 * Support for Column Header resizing
64446 * @param {Object} config
64449 // This is a support class used internally by the Grid components
64450 Roo.grid.SplitDragZone = function(grid, hd, hd2){
64452 this.view = grid.getView();
64453 this.proxy = this.view.resizeProxy;
64454 Roo.grid.SplitDragZone.superclass.constructor.call(
64457 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
64459 dragElId : Roo.id(this.proxy.dom),
64464 this.setHandleElId(Roo.id(hd));
64465 if (hd2 !== false) {
64466 this.setOuterHandleElId(Roo.id(hd2));
64469 this.scroll = false;
64471 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
64472 fly: Roo.Element.fly,
64474 b4StartDrag : function(x, y){
64475 this.view.headersDisabled = true;
64476 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
64477 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
64479 this.proxy.setHeight(h);
64481 // for old system colWidth really stored the actual width?
64482 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
64483 // which in reality did not work.. - it worked only for fixed sizes
64484 // for resizable we need to use actual sizes.
64485 var w = this.cm.getColumnWidth(this.cellIndex);
64486 if (!this.view.mainWrap) {
64488 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
64493 // this was w-this.grid.minColumnWidth;
64494 // doesnt really make sense? - w = thie curren width or the rendered one?
64495 var minw = Math.max(w-this.grid.minColumnWidth, 0);
64496 this.resetConstraints();
64497 this.setXConstraint(minw, 1000);
64498 this.setYConstraint(0, 0);
64499 this.minX = x - minw;
64500 this.maxX = x + 1000;
64502 if (!this.view.mainWrap) { // this is Bootstrap code..
64503 this.getDragEl().style.display='block';
64506 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
64510 handleMouseDown : function(e){
64511 ev = Roo.EventObject.setEvent(e);
64512 var t = this.fly(ev.getTarget());
64513 if(t.hasClass("x-grid-split")){
64514 this.cellIndex = this.view.getCellIndex(t.dom);
64515 this.split = t.dom;
64516 this.cm = this.grid.colModel;
64517 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
64518 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
64523 endDrag : function(e){
64524 this.view.headersDisabled = false;
64525 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
64526 var diff = endX - this.startPos;
64528 var w = this.cm.getColumnWidth(this.cellIndex);
64529 if (!this.view.mainWrap) {
64532 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
64535 autoOffset : function(){
64536 this.setDelta(0,0);
64540 * Ext JS Library 1.1.1
64541 * Copyright(c) 2006-2007, Ext JS, LLC.
64543 * Originally Released Under LGPL - original licence link has changed is not relivant.
64546 * <script type="text/javascript">
64550 // This is a support class used internally by the Grid components
64551 Roo.grid.GridDragZone = function(grid, config){
64552 this.view = grid.getView();
64553 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
64554 if(this.view.lockedBody){
64555 this.setHandleElId(Roo.id(this.view.mainBody.dom));
64556 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
64558 this.scroll = false;
64560 this.ddel = document.createElement('div');
64561 this.ddel.className = 'x-grid-dd-wrap';
64564 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
64565 ddGroup : "GridDD",
64567 getDragData : function(e){
64568 var t = Roo.lib.Event.getTarget(e);
64569 var rowIndex = this.view.findRowIndex(t);
64570 var sm = this.grid.selModel;
64572 //Roo.log(rowIndex);
64574 if (sm.getSelectedCell) {
64575 // cell selection..
64576 if (!sm.getSelectedCell()) {
64579 if (rowIndex != sm.getSelectedCell()[0]) {
64584 if (sm.getSelections && sm.getSelections().length < 1) {
64589 // before it used to all dragging of unseleted... - now we dont do that.
64590 if(rowIndex !== false){
64595 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
64597 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
64600 if (e.hasModifier()){
64601 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
64604 Roo.log("getDragData");
64609 rowIndex: rowIndex,
64610 selections: sm.getSelections ? sm.getSelections() : (
64611 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
64618 onInitDrag : function(e){
64619 var data = this.dragData;
64620 this.ddel.innerHTML = this.grid.getDragDropText();
64621 this.proxy.update(this.ddel);
64622 // fire start drag?
64625 afterRepair : function(){
64626 this.dragging = false;
64629 getRepairXY : function(e, data){
64633 onEndDrag : function(data, e){
64637 onValidDrop : function(dd, e, id){
64642 beforeInvalidDrop : function(e, id){
64647 * Ext JS Library 1.1.1
64648 * Copyright(c) 2006-2007, Ext JS, LLC.
64650 * Originally Released Under LGPL - original licence link has changed is not relivant.
64653 * <script type="text/javascript">
64658 * @class Roo.grid.ColumnModel
64659 * @extends Roo.util.Observable
64660 * This is the default implementation of a ColumnModel used by the Grid. It defines
64661 * the columns in the grid.
64664 var colModel = new Roo.grid.ColumnModel([
64665 {header: "Ticker", width: 60, sortable: true, locked: true},
64666 {header: "Company Name", width: 150, sortable: true},
64667 {header: "Market Cap.", width: 100, sortable: true},
64668 {header: "$ Sales", width: 100, sortable: true, renderer: money},
64669 {header: "Employees", width: 100, sortable: true, resizable: false}
64674 * The config options listed for this class are options which may appear in each
64675 * individual column definition.
64676 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
64678 * @param {Object} config An Array of column config objects. See this class's
64679 * config objects for details.
64681 Roo.grid.ColumnModel = function(config){
64683 * The config passed into the constructor
64685 this.config = []; //config;
64688 // if no id, create one
64689 // if the column does not have a dataIndex mapping,
64690 // map it to the order it is in the config
64691 for(var i = 0, len = config.length; i < len; i++){
64692 this.addColumn(config[i]);
64697 * The width of columns which have no width specified (defaults to 100)
64700 this.defaultWidth = 100;
64703 * Default sortable of columns which have no sortable specified (defaults to false)
64706 this.defaultSortable = false;
64710 * @event widthchange
64711 * Fires when the width of a column changes.
64712 * @param {ColumnModel} this
64713 * @param {Number} columnIndex The column index
64714 * @param {Number} newWidth The new width
64716 "widthchange": true,
64718 * @event headerchange
64719 * Fires when the text of a header changes.
64720 * @param {ColumnModel} this
64721 * @param {Number} columnIndex The column index
64722 * @param {Number} newText The new header text
64724 "headerchange": true,
64726 * @event hiddenchange
64727 * Fires when a column is hidden or "unhidden".
64728 * @param {ColumnModel} this
64729 * @param {Number} columnIndex The column index
64730 * @param {Boolean} hidden true if hidden, false otherwise
64732 "hiddenchange": true,
64734 * @event columnmoved
64735 * Fires when a column is moved.
64736 * @param {ColumnModel} this
64737 * @param {Number} oldIndex
64738 * @param {Number} newIndex
64740 "columnmoved" : true,
64742 * @event columlockchange
64743 * Fires when a column's locked state is changed
64744 * @param {ColumnModel} this
64745 * @param {Number} colIndex
64746 * @param {Boolean} locked true if locked
64748 "columnlockchange" : true
64750 Roo.grid.ColumnModel.superclass.constructor.call(this);
64752 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
64754 * @cfg {String} header [required] The header text to display in the Grid view.
64757 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
64760 * @cfg {String} smHeader Header at Bootsrap Small width
64763 * @cfg {String} mdHeader Header at Bootsrap Medium width
64766 * @cfg {String} lgHeader Header at Bootsrap Large width
64769 * @cfg {String} xlHeader Header at Bootsrap extra Large width
64772 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
64773 * {@link Roo.data.Record} definition from which to draw the column's value. If not
64774 * specified, the column's index is used as an index into the Record's data Array.
64777 * @cfg {Number} width The initial width in pixels of the column. Using this
64778 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
64781 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
64782 * Defaults to the value of the {@link #defaultSortable} property.
64783 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
64786 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
64789 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
64792 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
64795 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
64798 * @cfg {Function} renderer A function used to generate HTML markup for a cell
64799 * given the cell's data value. See {@link #setRenderer}. If not specified, the
64800 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
64801 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
64804 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
64807 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
64810 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
64813 * @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)
64816 * @cfg {String} tooltip mouse over tooltip text
64819 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
64822 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
64825 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
64828 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
64831 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
64834 * Returns the id of the column at the specified index.
64835 * @param {Number} index The column index
64836 * @return {String} the id
64838 getColumnId : function(index){
64839 return this.config[index].id;
64843 * Returns the column for a specified id.
64844 * @param {String} id The column id
64845 * @return {Object} the column
64847 getColumnById : function(id){
64848 return this.lookup[id];
64853 * Returns the column Object for a specified dataIndex.
64854 * @param {String} dataIndex The column dataIndex
64855 * @return {Object|Boolean} the column or false if not found
64857 getColumnByDataIndex: function(dataIndex){
64858 var index = this.findColumnIndex(dataIndex);
64859 return index > -1 ? this.config[index] : false;
64863 * Returns the index for a specified column id.
64864 * @param {String} id The column id
64865 * @return {Number} the index, or -1 if not found
64867 getIndexById : function(id){
64868 for(var i = 0, len = this.config.length; i < len; i++){
64869 if(this.config[i].id == id){
64877 * Returns the index for a specified column dataIndex.
64878 * @param {String} dataIndex The column dataIndex
64879 * @return {Number} the index, or -1 if not found
64882 findColumnIndex : function(dataIndex){
64883 for(var i = 0, len = this.config.length; i < len; i++){
64884 if(this.config[i].dataIndex == dataIndex){
64892 moveColumn : function(oldIndex, newIndex){
64893 var c = this.config[oldIndex];
64894 this.config.splice(oldIndex, 1);
64895 this.config.splice(newIndex, 0, c);
64896 this.dataMap = null;
64897 this.fireEvent("columnmoved", this, oldIndex, newIndex);
64900 isLocked : function(colIndex){
64901 return this.config[colIndex].locked === true;
64904 setLocked : function(colIndex, value, suppressEvent){
64905 if(this.isLocked(colIndex) == value){
64908 this.config[colIndex].locked = value;
64909 if(!suppressEvent){
64910 this.fireEvent("columnlockchange", this, colIndex, value);
64914 getTotalLockedWidth : function(){
64915 var totalWidth = 0;
64916 for(var i = 0; i < this.config.length; i++){
64917 if(this.isLocked(i) && !this.isHidden(i)){
64918 this.totalWidth += this.getColumnWidth(i);
64924 getLockedCount : function(){
64925 for(var i = 0, len = this.config.length; i < len; i++){
64926 if(!this.isLocked(i)){
64931 return this.config.length;
64935 * Returns the number of columns.
64938 getColumnCount : function(visibleOnly){
64939 if(visibleOnly === true){
64941 for(var i = 0, len = this.config.length; i < len; i++){
64942 if(!this.isHidden(i)){
64948 return this.config.length;
64952 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
64953 * @param {Function} fn
64954 * @param {Object} scope (optional)
64955 * @return {Array} result
64957 getColumnsBy : function(fn, scope){
64959 for(var i = 0, len = this.config.length; i < len; i++){
64960 var c = this.config[i];
64961 if(fn.call(scope||this, c, i) === true){
64969 * Returns true if the specified column is sortable.
64970 * @param {Number} col The column index
64971 * @return {Boolean}
64973 isSortable : function(col){
64974 if(typeof this.config[col].sortable == "undefined"){
64975 return this.defaultSortable;
64977 return this.config[col].sortable;
64981 * Returns the rendering (formatting) function defined for the column.
64982 * @param {Number} col The column index.
64983 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
64985 getRenderer : function(col){
64986 if(!this.config[col].renderer){
64987 return Roo.grid.ColumnModel.defaultRenderer;
64989 return this.config[col].renderer;
64993 * Sets the rendering (formatting) function for a column.
64994 * @param {Number} col The column index
64995 * @param {Function} fn The function to use to process the cell's raw data
64996 * to return HTML markup for the grid view. The render function is called with
64997 * the following parameters:<ul>
64998 * <li>Data value.</li>
64999 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
65000 * <li>css A CSS style string to apply to the table cell.</li>
65001 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
65002 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
65003 * <li>Row index</li>
65004 * <li>Column index</li>
65005 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
65007 setRenderer : function(col, fn){
65008 this.config[col].renderer = fn;
65012 * Returns the width for the specified column.
65013 * @param {Number} col The column index
65014 * @param (optional) {String} gridSize bootstrap width size.
65017 getColumnWidth : function(col, gridSize)
65019 var cfg = this.config[col];
65021 if (typeof(gridSize) == 'undefined') {
65022 return cfg.width * 1 || this.defaultWidth;
65024 if (gridSize === false) { // if we set it..
65025 return cfg.width || false;
65027 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
65029 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
65030 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
65033 return cfg[ sizes[i] ];
65040 * Sets the width for a column.
65041 * @param {Number} col The column index
65042 * @param {Number} width The new width
65044 setColumnWidth : function(col, width, suppressEvent){
65045 this.config[col].width = width;
65046 this.totalWidth = null;
65047 if(!suppressEvent){
65048 this.fireEvent("widthchange", this, col, width);
65053 * Returns the total width of all columns.
65054 * @param {Boolean} includeHidden True to include hidden column widths
65057 getTotalWidth : function(includeHidden){
65058 if(!this.totalWidth){
65059 this.totalWidth = 0;
65060 for(var i = 0, len = this.config.length; i < len; i++){
65061 if(includeHidden || !this.isHidden(i)){
65062 this.totalWidth += this.getColumnWidth(i);
65066 return this.totalWidth;
65070 * Returns the header for the specified column.
65071 * @param {Number} col The column index
65074 getColumnHeader : function(col){
65075 return this.config[col].header;
65079 * Sets the header for a column.
65080 * @param {Number} col The column index
65081 * @param {String} header The new header
65083 setColumnHeader : function(col, header){
65084 this.config[col].header = header;
65085 this.fireEvent("headerchange", this, col, header);
65089 * Returns the tooltip for the specified column.
65090 * @param {Number} col The column index
65093 getColumnTooltip : function(col){
65094 return this.config[col].tooltip;
65097 * Sets the tooltip for a column.
65098 * @param {Number} col The column index
65099 * @param {String} tooltip The new tooltip
65101 setColumnTooltip : function(col, tooltip){
65102 this.config[col].tooltip = tooltip;
65106 * Returns the dataIndex for the specified column.
65107 * @param {Number} col The column index
65110 getDataIndex : function(col){
65111 return this.config[col].dataIndex;
65115 * Sets the dataIndex for a column.
65116 * @param {Number} col The column index
65117 * @param {Number} dataIndex The new dataIndex
65119 setDataIndex : function(col, dataIndex){
65120 this.config[col].dataIndex = dataIndex;
65126 * Returns true if the cell is editable.
65127 * @param {Number} colIndex The column index
65128 * @param {Number} rowIndex The row index - this is nto actually used..?
65129 * @return {Boolean}
65131 isCellEditable : function(colIndex, rowIndex){
65132 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
65136 * Returns the editor defined for the cell/column.
65137 * return false or null to disable editing.
65138 * @param {Number} colIndex The column index
65139 * @param {Number} rowIndex The row index
65142 getCellEditor : function(colIndex, rowIndex){
65143 return this.config[colIndex].editor;
65147 * Sets if a column is editable.
65148 * @param {Number} col The column index
65149 * @param {Boolean} editable True if the column is editable
65151 setEditable : function(col, editable){
65152 this.config[col].editable = editable;
65157 * Returns true if the column is hidden.
65158 * @param {Number} colIndex The column index
65159 * @return {Boolean}
65161 isHidden : function(colIndex){
65162 return this.config[colIndex].hidden;
65167 * Returns true if the column width cannot be changed
65169 isFixed : function(colIndex){
65170 return this.config[colIndex].fixed;
65174 * Returns true if the column can be resized
65175 * @return {Boolean}
65177 isResizable : function(colIndex){
65178 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
65181 * Sets if a column is hidden.
65182 * @param {Number} colIndex The column index
65183 * @param {Boolean} hidden True if the column is hidden
65185 setHidden : function(colIndex, hidden){
65186 this.config[colIndex].hidden = hidden;
65187 this.totalWidth = null;
65188 this.fireEvent("hiddenchange", this, colIndex, hidden);
65192 * Sets the editor for a column.
65193 * @param {Number} col The column index
65194 * @param {Object} editor The editor object
65196 setEditor : function(col, editor){
65197 this.config[col].editor = editor;
65200 * Add a column (experimental...) - defaults to adding to the end..
65201 * @param {Object} config
65203 addColumn : function(c)
65206 var i = this.config.length;
65207 this.config[i] = c;
65209 if(typeof c.dataIndex == "undefined"){
65212 if(typeof c.renderer == "string"){
65213 c.renderer = Roo.util.Format[c.renderer];
65215 if(typeof c.id == "undefined"){
65218 if(c.editor && c.editor.xtype){
65219 c.editor = Roo.factory(c.editor, Roo.grid);
65221 if(c.editor && c.editor.isFormField){
65222 c.editor = new Roo.grid.GridEditor(c.editor);
65224 this.lookup[c.id] = c;
65229 Roo.grid.ColumnModel.defaultRenderer = function(value)
65231 if(typeof value == "object") {
65234 if(typeof value == "string" && value.length < 1){
65238 return String.format("{0}", value);
65241 // Alias for backwards compatibility
65242 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
65245 * Ext JS Library 1.1.1
65246 * Copyright(c) 2006-2007, Ext JS, LLC.
65248 * Originally Released Under LGPL - original licence link has changed is not relivant.
65251 * <script type="text/javascript">
65255 * @class Roo.grid.AbstractSelectionModel
65256 * @extends Roo.util.Observable
65258 * Abstract base class for grid SelectionModels. It provides the interface that should be
65259 * implemented by descendant classes. This class should not be directly instantiated.
65262 Roo.grid.AbstractSelectionModel = function(){
65263 this.locked = false;
65264 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
65267 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
65268 /** @ignore Called by the grid automatically. Do not call directly. */
65269 init : function(grid){
65275 * Locks the selections.
65278 this.locked = true;
65282 * Unlocks the selections.
65284 unlock : function(){
65285 this.locked = false;
65289 * Returns true if the selections are locked.
65290 * @return {Boolean}
65292 isLocked : function(){
65293 return this.locked;
65297 * Ext JS Library 1.1.1
65298 * Copyright(c) 2006-2007, Ext JS, LLC.
65300 * Originally Released Under LGPL - original licence link has changed is not relivant.
65303 * <script type="text/javascript">
65306 * @extends Roo.grid.AbstractSelectionModel
65307 * @class Roo.grid.RowSelectionModel
65308 * The default SelectionModel used by {@link Roo.grid.Grid}.
65309 * It supports multiple selections and keyboard selection/navigation.
65311 * @param {Object} config
65313 Roo.grid.RowSelectionModel = function(config){
65314 Roo.apply(this, config);
65315 this.selections = new Roo.util.MixedCollection(false, function(o){
65320 this.lastActive = false;
65324 * @event selectionchange
65325 * Fires when the selection changes
65326 * @param {SelectionModel} this
65328 "selectionchange" : true,
65330 * @event afterselectionchange
65331 * Fires after the selection changes (eg. by key press or clicking)
65332 * @param {SelectionModel} this
65334 "afterselectionchange" : true,
65336 * @event beforerowselect
65337 * Fires when a row is selected being selected, return false to cancel.
65338 * @param {SelectionModel} this
65339 * @param {Number} rowIndex The selected index
65340 * @param {Boolean} keepExisting False if other selections will be cleared
65342 "beforerowselect" : true,
65345 * Fires when a row is selected.
65346 * @param {SelectionModel} this
65347 * @param {Number} rowIndex The selected index
65348 * @param {Roo.data.Record} r The record
65350 "rowselect" : true,
65352 * @event rowdeselect
65353 * Fires when a row is deselected.
65354 * @param {SelectionModel} this
65355 * @param {Number} rowIndex The selected index
65357 "rowdeselect" : true
65359 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
65360 this.locked = false;
65363 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
65365 * @cfg {Boolean} singleSelect
65366 * True to allow selection of only one row at a time (defaults to false)
65368 singleSelect : false,
65371 initEvents : function(){
65373 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
65374 this.grid.on("mousedown", this.handleMouseDown, this);
65375 }else{ // allow click to work like normal
65376 this.grid.on("rowclick", this.handleDragableRowClick, this);
65378 // bootstrap does not have a view..
65379 var view = this.grid.view ? this.grid.view : this.grid;
65380 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
65381 "up" : function(e){
65383 this.selectPrevious(e.shiftKey);
65384 }else if(this.last !== false && this.lastActive !== false){
65385 var last = this.last;
65386 this.selectRange(this.last, this.lastActive-1);
65387 view.focusRow(this.lastActive);
65388 if(last !== false){
65392 this.selectFirstRow();
65394 this.fireEvent("afterselectionchange", this);
65396 "down" : function(e){
65398 this.selectNext(e.shiftKey);
65399 }else if(this.last !== false && this.lastActive !== false){
65400 var last = this.last;
65401 this.selectRange(this.last, this.lastActive+1);
65402 view.focusRow(this.lastActive);
65403 if(last !== false){
65407 this.selectFirstRow();
65409 this.fireEvent("afterselectionchange", this);
65415 view.on("refresh", this.onRefresh, this);
65416 view.on("rowupdated", this.onRowUpdated, this);
65417 view.on("rowremoved", this.onRemove, this);
65421 onRefresh : function(){
65422 var ds = this.grid.ds, i, v = this.grid.view;
65423 var s = this.selections;
65424 s.each(function(r){
65425 if((i = ds.indexOfId(r.id)) != -1){
65427 s.add(ds.getAt(i)); // updating the selection relate data
65435 onRemove : function(v, index, r){
65436 this.selections.remove(r);
65440 onRowUpdated : function(v, index, r){
65441 if(this.isSelected(r)){
65442 v.onRowSelect(index);
65448 * @param {Array} records The records to select
65449 * @param {Boolean} keepExisting (optional) True to keep existing selections
65451 selectRecords : function(records, keepExisting){
65453 this.clearSelections();
65455 var ds = this.grid.ds;
65456 for(var i = 0, len = records.length; i < len; i++){
65457 this.selectRow(ds.indexOf(records[i]), true);
65462 * Gets the number of selected rows.
65465 getCount : function(){
65466 return this.selections.length;
65470 * Selects the first row in the grid.
65472 selectFirstRow : function(){
65477 * Select the last row.
65478 * @param {Boolean} keepExisting (optional) True to keep existing selections
65480 selectLastRow : function(keepExisting){
65481 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
65485 * Selects the row immediately following the last selected row.
65486 * @param {Boolean} keepExisting (optional) True to keep existing selections
65488 selectNext : function(keepExisting){
65489 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
65490 this.selectRow(this.last+1, keepExisting);
65491 var view = this.grid.view ? this.grid.view : this.grid;
65492 view.focusRow(this.last);
65497 * Selects the row that precedes the last selected row.
65498 * @param {Boolean} keepExisting (optional) True to keep existing selections
65500 selectPrevious : function(keepExisting){
65502 this.selectRow(this.last-1, keepExisting);
65503 var view = this.grid.view ? this.grid.view : this.grid;
65504 view.focusRow(this.last);
65509 * Returns the selected records
65510 * @return {Array} Array of selected records
65512 getSelections : function(){
65513 return [].concat(this.selections.items);
65517 * Returns the first selected record.
65520 getSelected : function(){
65521 return this.selections.itemAt(0);
65526 * Clears all selections.
65528 clearSelections : function(fast){
65533 var ds = this.grid.ds;
65534 var s = this.selections;
65535 s.each(function(r){
65536 this.deselectRow(ds.indexOfId(r.id));
65540 this.selections.clear();
65547 * Selects all rows.
65549 selectAll : function(){
65553 this.selections.clear();
65554 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
65555 this.selectRow(i, true);
65560 * Returns True if there is a selection.
65561 * @return {Boolean}
65563 hasSelection : function(){
65564 return this.selections.length > 0;
65568 * Returns True if the specified row is selected.
65569 * @param {Number/Record} record The record or index of the record to check
65570 * @return {Boolean}
65572 isSelected : function(index){
65573 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
65574 return (r && this.selections.key(r.id) ? true : false);
65578 * Returns True if the specified record id is selected.
65579 * @param {String} id The id of record to check
65580 * @return {Boolean}
65582 isIdSelected : function(id){
65583 return (this.selections.key(id) ? true : false);
65587 handleMouseDown : function(e, t)
65589 var view = this.grid.view ? this.grid.view : this.grid;
65591 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
65594 if(e.shiftKey && this.last !== false){
65595 var last = this.last;
65596 this.selectRange(last, rowIndex, e.ctrlKey);
65597 this.last = last; // reset the last
65598 view.focusRow(rowIndex);
65600 var isSelected = this.isSelected(rowIndex);
65601 if(e.button !== 0 && isSelected){
65602 view.focusRow(rowIndex);
65603 }else if(e.ctrlKey && isSelected){
65604 this.deselectRow(rowIndex);
65605 }else if(!isSelected){
65606 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
65607 view.focusRow(rowIndex);
65610 this.fireEvent("afterselectionchange", this);
65613 handleDragableRowClick : function(grid, rowIndex, e)
65615 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
65616 this.selectRow(rowIndex, false);
65617 var view = this.grid.view ? this.grid.view : this.grid;
65618 view.focusRow(rowIndex);
65619 this.fireEvent("afterselectionchange", this);
65624 * Selects multiple rows.
65625 * @param {Array} rows Array of the indexes of the row to select
65626 * @param {Boolean} keepExisting (optional) True to keep existing selections
65628 selectRows : function(rows, keepExisting){
65630 this.clearSelections();
65632 for(var i = 0, len = rows.length; i < len; i++){
65633 this.selectRow(rows[i], true);
65638 * Selects a range of rows. All rows in between startRow and endRow are also selected.
65639 * @param {Number} startRow The index of the first row in the range
65640 * @param {Number} endRow The index of the last row in the range
65641 * @param {Boolean} keepExisting (optional) True to retain existing selections
65643 selectRange : function(startRow, endRow, keepExisting){
65648 this.clearSelections();
65650 if(startRow <= endRow){
65651 for(var i = startRow; i <= endRow; i++){
65652 this.selectRow(i, true);
65655 for(var i = startRow; i >= endRow; i--){
65656 this.selectRow(i, true);
65662 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
65663 * @param {Number} startRow The index of the first row in the range
65664 * @param {Number} endRow The index of the last row in the range
65666 deselectRange : function(startRow, endRow, preventViewNotify){
65670 for(var i = startRow; i <= endRow; i++){
65671 this.deselectRow(i, preventViewNotify);
65677 * @param {Number} row The index of the row to select
65678 * @param {Boolean} keepExisting (optional) True to keep existing selections
65680 selectRow : function(index, keepExisting, preventViewNotify){
65681 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
65684 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
65685 if(!keepExisting || this.singleSelect){
65686 this.clearSelections();
65688 var r = this.grid.ds.getAt(index);
65689 this.selections.add(r);
65690 this.last = this.lastActive = index;
65691 if(!preventViewNotify){
65692 var view = this.grid.view ? this.grid.view : this.grid;
65693 view.onRowSelect(index);
65695 this.fireEvent("rowselect", this, index, r);
65696 this.fireEvent("selectionchange", this);
65702 * @param {Number} row The index of the row to deselect
65704 deselectRow : function(index, preventViewNotify){
65708 if(this.last == index){
65711 if(this.lastActive == index){
65712 this.lastActive = false;
65714 var r = this.grid.ds.getAt(index);
65715 this.selections.remove(r);
65716 if(!preventViewNotify){
65717 var view = this.grid.view ? this.grid.view : this.grid;
65718 view.onRowDeselect(index);
65720 this.fireEvent("rowdeselect", this, index);
65721 this.fireEvent("selectionchange", this);
65725 restoreLast : function(){
65727 this.last = this._last;
65732 acceptsNav : function(row, col, cm){
65733 return !cm.isHidden(col) && cm.isCellEditable(col, row);
65737 onEditorKey : function(field, e){
65738 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
65743 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65745 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65747 }else if(k == e.ENTER && !e.ctrlKey){
65751 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
65753 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
65755 }else if(k == e.ESC){
65759 g.startEditing(newCell[0], newCell[1]);
65764 * Ext JS Library 1.1.1
65765 * Copyright(c) 2006-2007, Ext JS, LLC.
65767 * Originally Released Under LGPL - original licence link has changed is not relivant.
65770 * <script type="text/javascript">
65773 * @class Roo.grid.CellSelectionModel
65774 * @extends Roo.grid.AbstractSelectionModel
65775 * This class provides the basic implementation for cell selection in a grid.
65777 * @param {Object} config The object containing the configuration of this model.
65778 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
65780 Roo.grid.CellSelectionModel = function(config){
65781 Roo.apply(this, config);
65783 this.selection = null;
65787 * @event beforerowselect
65788 * Fires before a cell is selected.
65789 * @param {SelectionModel} this
65790 * @param {Number} rowIndex The selected row index
65791 * @param {Number} colIndex The selected cell index
65793 "beforecellselect" : true,
65795 * @event cellselect
65796 * Fires when a cell is selected.
65797 * @param {SelectionModel} this
65798 * @param {Number} rowIndex The selected row index
65799 * @param {Number} colIndex The selected cell index
65801 "cellselect" : true,
65803 * @event selectionchange
65804 * Fires when the active selection changes.
65805 * @param {SelectionModel} this
65806 * @param {Object} selection null for no selection or an object (o) with two properties
65808 <li>o.record: the record object for the row the selection is in</li>
65809 <li>o.cell: An array of [rowIndex, columnIndex]</li>
65812 "selectionchange" : true,
65815 * Fires when the tab (or enter) was pressed on the last editable cell
65816 * You can use this to trigger add new row.
65817 * @param {SelectionModel} this
65821 * @event beforeeditnext
65822 * Fires before the next editable sell is made active
65823 * You can use this to skip to another cell or fire the tabend
65824 * if you set cell to false
65825 * @param {Object} eventdata object : { cell : [ row, col ] }
65827 "beforeeditnext" : true
65829 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
65832 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
65834 enter_is_tab: false,
65837 initEvents : function(){
65838 this.grid.on("mousedown", this.handleMouseDown, this);
65839 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
65840 var view = this.grid.view;
65841 view.on("refresh", this.onViewChange, this);
65842 view.on("rowupdated", this.onRowUpdated, this);
65843 view.on("beforerowremoved", this.clearSelections, this);
65844 view.on("beforerowsinserted", this.clearSelections, this);
65845 if(this.grid.isEditor){
65846 this.grid.on("beforeedit", this.beforeEdit, this);
65851 beforeEdit : function(e){
65852 this.select(e.row, e.column, false, true, e.record);
65856 onRowUpdated : function(v, index, r){
65857 if(this.selection && this.selection.record == r){
65858 v.onCellSelect(index, this.selection.cell[1]);
65863 onViewChange : function(){
65864 this.clearSelections(true);
65868 * Returns the currently selected cell,.
65869 * @return {Array} The selected cell (row, column) or null if none selected.
65871 getSelectedCell : function(){
65872 return this.selection ? this.selection.cell : null;
65876 * Clears all selections.
65877 * @param {Boolean} true to prevent the gridview from being notified about the change.
65879 clearSelections : function(preventNotify){
65880 var s = this.selection;
65882 if(preventNotify !== true){
65883 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
65885 this.selection = null;
65886 this.fireEvent("selectionchange", this, null);
65891 * Returns true if there is a selection.
65892 * @return {Boolean}
65894 hasSelection : function(){
65895 return this.selection ? true : false;
65899 handleMouseDown : function(e, t){
65900 var v = this.grid.getView();
65901 if(this.isLocked()){
65904 var row = v.findRowIndex(t);
65905 var cell = v.findCellIndex(t);
65906 if(row !== false && cell !== false){
65907 this.select(row, cell);
65913 * @param {Number} rowIndex
65914 * @param {Number} collIndex
65916 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
65917 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
65918 this.clearSelections();
65919 r = r || this.grid.dataSource.getAt(rowIndex);
65922 cell : [rowIndex, colIndex]
65924 if(!preventViewNotify){
65925 var v = this.grid.getView();
65926 v.onCellSelect(rowIndex, colIndex);
65927 if(preventFocus !== true){
65928 v.focusCell(rowIndex, colIndex);
65931 this.fireEvent("cellselect", this, rowIndex, colIndex);
65932 this.fireEvent("selectionchange", this, this.selection);
65937 isSelectable : function(rowIndex, colIndex, cm){
65938 return !cm.isHidden(colIndex);
65942 handleKeyDown : function(e){
65943 //Roo.log('Cell Sel Model handleKeyDown');
65944 if(!e.isNavKeyPress()){
65947 var g = this.grid, s = this.selection;
65950 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
65952 this.select(cell[0], cell[1]);
65957 var walk = function(row, col, step){
65958 return g.walkCells(row, col, step, sm.isSelectable, sm);
65960 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
65967 // handled by onEditorKey
65968 if (g.isEditor && g.editing) {
65972 newCell = walk(r, c-1, -1);
65974 newCell = walk(r, c+1, 1);
65979 newCell = walk(r+1, c, 1);
65983 newCell = walk(r-1, c, -1);
65987 newCell = walk(r, c+1, 1);
65991 newCell = walk(r, c-1, -1);
65996 if(g.isEditor && !g.editing){
65997 g.startEditing(r, c);
66006 this.select(newCell[0], newCell[1]);
66012 acceptsNav : function(row, col, cm){
66013 return !cm.isHidden(col) && cm.isCellEditable(col, row);
66017 * @param {Number} field (not used) - as it's normally used as a listener
66018 * @param {Number} e - event - fake it by using
66020 * var e = Roo.EventObjectImpl.prototype;
66021 * e.keyCode = e.TAB
66025 onEditorKey : function(field, e){
66027 var k = e.getKey(),
66030 ed = g.activeEditor,
66032 ///Roo.log('onEditorKey' + k);
66035 if (this.enter_is_tab && k == e.ENTER) {
66041 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
66043 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
66049 } else if(k == e.ENTER && !e.ctrlKey){
66052 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
66054 } else if(k == e.ESC){
66059 var ecall = { cell : newCell, forward : forward };
66060 this.fireEvent('beforeeditnext', ecall );
66061 newCell = ecall.cell;
66062 forward = ecall.forward;
66066 //Roo.log('next cell after edit');
66067 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
66068 } else if (forward) {
66069 // tabbed past last
66070 this.fireEvent.defer(100, this, ['tabend',this]);
66075 * Ext JS Library 1.1.1
66076 * Copyright(c) 2006-2007, Ext JS, LLC.
66078 * Originally Released Under LGPL - original licence link has changed is not relivant.
66081 * <script type="text/javascript">
66085 * @class Roo.grid.EditorGrid
66086 * @extends Roo.grid.Grid
66087 * Class for creating and editable grid.
66088 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66089 * The container MUST have some type of size defined for the grid to fill. The container will be
66090 * automatically set to position relative if it isn't already.
66091 * @param {Object} dataSource The data model to bind to
66092 * @param {Object} colModel The column model with info about this grid's columns
66094 Roo.grid.EditorGrid = function(container, config){
66095 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
66096 this.getGridEl().addClass("xedit-grid");
66098 if(!this.selModel){
66099 this.selModel = new Roo.grid.CellSelectionModel();
66102 this.activeEditor = null;
66106 * @event beforeedit
66107 * Fires before cell editing is triggered. The edit event object has the following properties <br />
66108 * <ul style="padding:5px;padding-left:16px;">
66109 * <li>grid - This grid</li>
66110 * <li>record - The record being edited</li>
66111 * <li>field - The field name being edited</li>
66112 * <li>value - The value for the field being edited.</li>
66113 * <li>row - The grid row index</li>
66114 * <li>column - The grid column index</li>
66115 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
66117 * @param {Object} e An edit event (see above for description)
66119 "beforeedit" : true,
66122 * Fires after a cell is edited. <br />
66123 * <ul style="padding:5px;padding-left:16px;">
66124 * <li>grid - This grid</li>
66125 * <li>record - The record being edited</li>
66126 * <li>field - The field name being edited</li>
66127 * <li>value - The value being set</li>
66128 * <li>originalValue - The original value for the field, before the edit.</li>
66129 * <li>row - The grid row index</li>
66130 * <li>column - The grid column index</li>
66132 * @param {Object} e An edit event (see above for description)
66134 "afteredit" : true,
66136 * @event validateedit
66137 * Fires after a cell is edited, but before the value is set in the record.
66138 * You can use this to modify the value being set in the field, Return false
66139 * to cancel the change. The edit event object has the following properties <br />
66140 * <ul style="padding:5px;padding-left:16px;">
66141 * <li>editor - This editor</li>
66142 * <li>grid - This grid</li>
66143 * <li>record - The record being edited</li>
66144 * <li>field - The field name being edited</li>
66145 * <li>value - The value being set</li>
66146 * <li>originalValue - The original value for the field, before the edit.</li>
66147 * <li>row - The grid row index</li>
66148 * <li>column - The grid column index</li>
66149 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
66151 * @param {Object} e An edit event (see above for description)
66153 "validateedit" : true
66155 this.on("bodyscroll", this.stopEditing, this);
66156 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
66159 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
66161 * @cfg {Number} clicksToEdit
66162 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
66169 trackMouseOver: false, // causes very odd FF errors
66171 onCellDblClick : function(g, row, col){
66172 this.startEditing(row, col);
66175 onEditComplete : function(ed, value, startValue){
66176 this.editing = false;
66177 this.activeEditor = null;
66178 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
66180 var field = this.colModel.getDataIndex(ed.col);
66185 originalValue: startValue,
66192 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
66195 if(String(value) !== String(startValue)){
66197 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
66198 r.set(field, e.value);
66199 // if we are dealing with a combo box..
66200 // then we also set the 'name' colum to be the displayField
66201 if (ed.field.displayField && ed.field.name) {
66202 r.set(ed.field.name, ed.field.el.dom.value);
66205 delete e.cancel; //?? why!!!
66206 this.fireEvent("afteredit", e);
66209 this.fireEvent("afteredit", e); // always fire it!
66211 this.view.focusCell(ed.row, ed.col);
66215 * Starts editing the specified for the specified row/column
66216 * @param {Number} rowIndex
66217 * @param {Number} colIndex
66219 startEditing : function(row, col){
66220 this.stopEditing();
66221 if(this.colModel.isCellEditable(col, row)){
66222 this.view.ensureVisible(row, col, true);
66224 var r = this.dataSource.getAt(row);
66225 var field = this.colModel.getDataIndex(col);
66226 var cell = Roo.get(this.view.getCell(row,col));
66231 value: r.data[field],
66236 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
66237 this.editing = true;
66238 var ed = this.colModel.getCellEditor(col, row);
66244 ed.render(ed.parentEl || document.body);
66250 (function(){ // complex but required for focus issues in safari, ie and opera
66254 ed.on("complete", this.onEditComplete, this, {single: true});
66255 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
66256 this.activeEditor = ed;
66257 var v = r.data[field];
66258 ed.startEdit(this.view.getCell(row, col), v);
66259 // combo's with 'displayField and name set
66260 if (ed.field.displayField && ed.field.name) {
66261 ed.field.el.dom.value = r.data[ed.field.name];
66265 }).defer(50, this);
66271 * Stops any active editing
66273 stopEditing : function(){
66274 if(this.activeEditor){
66275 this.activeEditor.completeEdit();
66277 this.activeEditor = null;
66281 * Called to get grid's drag proxy text, by default returns this.ddText.
66284 getDragDropText : function(){
66285 var count = this.selModel.getSelectedCell() ? 1 : 0;
66286 return String.format(this.ddText, count, count == 1 ? '' : 's');
66291 * Ext JS Library 1.1.1
66292 * Copyright(c) 2006-2007, Ext JS, LLC.
66294 * Originally Released Under LGPL - original licence link has changed is not relivant.
66297 * <script type="text/javascript">
66300 // private - not really -- you end up using it !
66301 // This is a support class used internally by the Grid components
66304 * @class Roo.grid.GridEditor
66305 * @extends Roo.Editor
66306 * Class for creating and editable grid elements.
66307 * @param {Object} config any settings (must include field)
66309 Roo.grid.GridEditor = function(field, config){
66310 if (!config && field.field) {
66312 field = Roo.factory(config.field, Roo.form);
66314 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
66315 field.monitorTab = false;
66318 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
66321 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
66324 alignment: "tl-tl",
66327 cls: "x-small-editor x-grid-editor",
66332 * Ext JS Library 1.1.1
66333 * Copyright(c) 2006-2007, Ext JS, LLC.
66335 * Originally Released Under LGPL - original licence link has changed is not relivant.
66338 * <script type="text/javascript">
66343 Roo.grid.PropertyRecord = Roo.data.Record.create([
66344 {name:'name',type:'string'}, 'value'
66348 Roo.grid.PropertyStore = function(grid, source){
66350 this.store = new Roo.data.Store({
66351 recordType : Roo.grid.PropertyRecord
66353 this.store.on('update', this.onUpdate, this);
66355 this.setSource(source);
66357 Roo.grid.PropertyStore.superclass.constructor.call(this);
66362 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
66363 setSource : function(o){
66365 this.store.removeAll();
66368 if(this.isEditableValue(o[k])){
66369 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
66372 this.store.loadRecords({records: data}, {}, true);
66375 onUpdate : function(ds, record, type){
66376 if(type == Roo.data.Record.EDIT){
66377 var v = record.data['value'];
66378 var oldValue = record.modified['value'];
66379 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
66380 this.source[record.id] = v;
66382 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
66389 getProperty : function(row){
66390 return this.store.getAt(row);
66393 isEditableValue: function(val){
66394 if(val && val instanceof Date){
66396 }else if(typeof val == 'object' || typeof val == 'function'){
66402 setValue : function(prop, value){
66403 this.source[prop] = value;
66404 this.store.getById(prop).set('value', value);
66407 getSource : function(){
66408 return this.source;
66412 Roo.grid.PropertyColumnModel = function(grid, store){
66415 g.PropertyColumnModel.superclass.constructor.call(this, [
66416 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
66417 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
66419 this.store = store;
66420 this.bselect = Roo.DomHelper.append(document.body, {
66421 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
66422 {tag: 'option', value: 'true', html: 'true'},
66423 {tag: 'option', value: 'false', html: 'false'}
66426 Roo.id(this.bselect);
66429 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
66430 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
66431 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
66432 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
66433 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
66435 this.renderCellDelegate = this.renderCell.createDelegate(this);
66436 this.renderPropDelegate = this.renderProp.createDelegate(this);
66439 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
66443 valueText : 'Value',
66445 dateFormat : 'm/j/Y',
66448 renderDate : function(dateVal){
66449 return dateVal.dateFormat(this.dateFormat);
66452 renderBool : function(bVal){
66453 return bVal ? 'true' : 'false';
66456 isCellEditable : function(colIndex, rowIndex){
66457 return colIndex == 1;
66460 getRenderer : function(col){
66462 this.renderCellDelegate : this.renderPropDelegate;
66465 renderProp : function(v){
66466 return this.getPropertyName(v);
66469 renderCell : function(val){
66471 if(val instanceof Date){
66472 rv = this.renderDate(val);
66473 }else if(typeof val == 'boolean'){
66474 rv = this.renderBool(val);
66476 return Roo.util.Format.htmlEncode(rv);
66479 getPropertyName : function(name){
66480 var pn = this.grid.propertyNames;
66481 return pn && pn[name] ? pn[name] : name;
66484 getCellEditor : function(colIndex, rowIndex){
66485 var p = this.store.getProperty(rowIndex);
66486 var n = p.data['name'], val = p.data['value'];
66488 if(typeof(this.grid.customEditors[n]) == 'string'){
66489 return this.editors[this.grid.customEditors[n]];
66491 if(typeof(this.grid.customEditors[n]) != 'undefined'){
66492 return this.grid.customEditors[n];
66494 if(val instanceof Date){
66495 return this.editors['date'];
66496 }else if(typeof val == 'number'){
66497 return this.editors['number'];
66498 }else if(typeof val == 'boolean'){
66499 return this.editors['boolean'];
66501 return this.editors['string'];
66507 * @class Roo.grid.PropertyGrid
66508 * @extends Roo.grid.EditorGrid
66509 * This class represents the interface of a component based property grid control.
66510 * <br><br>Usage:<pre><code>
66511 var grid = new Roo.grid.PropertyGrid("my-container-id", {
66519 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66520 * The container MUST have some type of size defined for the grid to fill. The container will be
66521 * automatically set to position relative if it isn't already.
66522 * @param {Object} config A config object that sets properties on this grid.
66524 Roo.grid.PropertyGrid = function(container, config){
66525 config = config || {};
66526 var store = new Roo.grid.PropertyStore(this);
66527 this.store = store;
66528 var cm = new Roo.grid.PropertyColumnModel(this, store);
66529 store.store.sort('name', 'ASC');
66530 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
66533 enableColLock:false,
66534 enableColumnMove:false,
66536 trackMouseOver: false,
66539 this.getGridEl().addClass('x-props-grid');
66540 this.lastEditRow = null;
66541 this.on('columnresize', this.onColumnResize, this);
66544 * @event beforepropertychange
66545 * Fires before a property changes (return false to stop?)
66546 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66547 * @param {String} id Record Id
66548 * @param {String} newval New Value
66549 * @param {String} oldval Old Value
66551 "beforepropertychange": true,
66553 * @event propertychange
66554 * Fires after a property changes
66555 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66556 * @param {String} id Record Id
66557 * @param {String} newval New Value
66558 * @param {String} oldval Old Value
66560 "propertychange": true
66562 this.customEditors = this.customEditors || {};
66564 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
66567 * @cfg {Object} customEditors map of colnames=> custom editors.
66568 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
66569 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
66570 * false disables editing of the field.
66574 * @cfg {Object} propertyNames map of property Names to their displayed value
66577 render : function(){
66578 Roo.grid.PropertyGrid.superclass.render.call(this);
66579 this.autoSize.defer(100, this);
66582 autoSize : function(){
66583 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
66585 this.view.fitColumns();
66589 onColumnResize : function(){
66590 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
66594 * Sets the data for the Grid
66595 * accepts a Key => Value object of all the elements avaiable.
66596 * @param {Object} data to appear in grid.
66598 setSource : function(source){
66599 this.store.setSource(source);
66603 * Gets all the data from the grid.
66604 * @return {Object} data data stored in grid
66606 getSource : function(){
66607 return this.store.getSource();
66616 * @class Roo.grid.Calendar
66617 * @extends Roo.grid.Grid
66618 * This class extends the Grid to provide a calendar widget
66619 * <br><br>Usage:<pre><code>
66620 var grid = new Roo.grid.Calendar("my-container-id", {
66623 selModel: mySelectionModel,
66624 autoSizeColumns: true,
66625 monitorWindowResize: false,
66626 trackMouseOver: true
66627 eventstore : real data store..
66633 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66634 * The container MUST have some type of size defined for the grid to fill. The container will be
66635 * automatically set to position relative if it isn't already.
66636 * @param {Object} config A config object that sets properties on this grid.
66638 Roo.grid.Calendar = function(container, config){
66639 // initialize the container
66640 this.container = Roo.get(container);
66641 this.container.update("");
66642 this.container.setStyle("overflow", "hidden");
66643 this.container.addClass('x-grid-container');
66645 this.id = this.container.id;
66647 Roo.apply(this, config);
66648 // check and correct shorthanded configs
66652 for (var r = 0;r < 6;r++) {
66655 for (var c =0;c < 7;c++) {
66659 if (this.eventStore) {
66660 this.eventStore= Roo.factory(this.eventStore, Roo.data);
66661 this.eventStore.on('load',this.onLoad, this);
66662 this.eventStore.on('beforeload',this.clearEvents, this);
66666 this.dataSource = new Roo.data.Store({
66667 proxy: new Roo.data.MemoryProxy(rows),
66668 reader: new Roo.data.ArrayReader({}, [
66669 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
66672 this.dataSource.load();
66673 this.ds = this.dataSource;
66674 this.ds.xmodule = this.xmodule || false;
66677 var cellRender = function(v,x,r)
66679 return String.format(
66680 '<div class="fc-day fc-widget-content"><div>' +
66681 '<div class="fc-event-container"></div>' +
66682 '<div class="fc-day-number">{0}</div>'+
66684 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
66685 '</div></div>', v);
66690 this.colModel = new Roo.grid.ColumnModel( [
66692 xtype: 'ColumnModel',
66694 dataIndex : 'weekday0',
66696 renderer : cellRender
66699 xtype: 'ColumnModel',
66701 dataIndex : 'weekday1',
66703 renderer : cellRender
66706 xtype: 'ColumnModel',
66708 dataIndex : 'weekday2',
66709 header : 'Tuesday',
66710 renderer : cellRender
66713 xtype: 'ColumnModel',
66715 dataIndex : 'weekday3',
66716 header : 'Wednesday',
66717 renderer : cellRender
66720 xtype: 'ColumnModel',
66722 dataIndex : 'weekday4',
66723 header : 'Thursday',
66724 renderer : cellRender
66727 xtype: 'ColumnModel',
66729 dataIndex : 'weekday5',
66731 renderer : cellRender
66734 xtype: 'ColumnModel',
66736 dataIndex : 'weekday6',
66737 header : 'Saturday',
66738 renderer : cellRender
66741 this.cm = this.colModel;
66742 this.cm.xmodule = this.xmodule || false;
66746 //this.selModel = new Roo.grid.CellSelectionModel();
66747 //this.sm = this.selModel;
66748 //this.selModel.init(this);
66752 this.container.setWidth(this.width);
66756 this.container.setHeight(this.height);
66763 * The raw click event for the entire grid.
66764 * @param {Roo.EventObject} e
66769 * The raw dblclick event for the entire grid.
66770 * @param {Roo.EventObject} e
66774 * @event contextmenu
66775 * The raw contextmenu event for the entire grid.
66776 * @param {Roo.EventObject} e
66778 "contextmenu" : true,
66781 * The raw mousedown event for the entire grid.
66782 * @param {Roo.EventObject} e
66784 "mousedown" : true,
66787 * The raw mouseup event for the entire grid.
66788 * @param {Roo.EventObject} e
66793 * The raw mouseover event for the entire grid.
66794 * @param {Roo.EventObject} e
66796 "mouseover" : true,
66799 * The raw mouseout event for the entire grid.
66800 * @param {Roo.EventObject} e
66805 * The raw keypress event for the entire grid.
66806 * @param {Roo.EventObject} e
66811 * The raw keydown event for the entire grid.
66812 * @param {Roo.EventObject} e
66820 * Fires when a cell is clicked
66821 * @param {Grid} this
66822 * @param {Number} rowIndex
66823 * @param {Number} columnIndex
66824 * @param {Roo.EventObject} e
66826 "cellclick" : true,
66828 * @event celldblclick
66829 * Fires when a cell is double clicked
66830 * @param {Grid} this
66831 * @param {Number} rowIndex
66832 * @param {Number} columnIndex
66833 * @param {Roo.EventObject} e
66835 "celldblclick" : true,
66838 * Fires when a row is clicked
66839 * @param {Grid} this
66840 * @param {Number} rowIndex
66841 * @param {Roo.EventObject} e
66845 * @event rowdblclick
66846 * Fires when a row is double clicked
66847 * @param {Grid} this
66848 * @param {Number} rowIndex
66849 * @param {Roo.EventObject} e
66851 "rowdblclick" : true,
66853 * @event headerclick
66854 * Fires when a header is clicked
66855 * @param {Grid} this
66856 * @param {Number} columnIndex
66857 * @param {Roo.EventObject} e
66859 "headerclick" : true,
66861 * @event headerdblclick
66862 * Fires when a header cell is double clicked
66863 * @param {Grid} this
66864 * @param {Number} columnIndex
66865 * @param {Roo.EventObject} e
66867 "headerdblclick" : true,
66869 * @event rowcontextmenu
66870 * Fires when a row is right clicked
66871 * @param {Grid} this
66872 * @param {Number} rowIndex
66873 * @param {Roo.EventObject} e
66875 "rowcontextmenu" : true,
66877 * @event cellcontextmenu
66878 * Fires when a cell is right clicked
66879 * @param {Grid} this
66880 * @param {Number} rowIndex
66881 * @param {Number} cellIndex
66882 * @param {Roo.EventObject} e
66884 "cellcontextmenu" : true,
66886 * @event headercontextmenu
66887 * Fires when a header is right clicked
66888 * @param {Grid} this
66889 * @param {Number} columnIndex
66890 * @param {Roo.EventObject} e
66892 "headercontextmenu" : true,
66894 * @event bodyscroll
66895 * Fires when the body element is scrolled
66896 * @param {Number} scrollLeft
66897 * @param {Number} scrollTop
66899 "bodyscroll" : true,
66901 * @event columnresize
66902 * Fires when the user resizes a column
66903 * @param {Number} columnIndex
66904 * @param {Number} newSize
66906 "columnresize" : true,
66908 * @event columnmove
66909 * Fires when the user moves a column
66910 * @param {Number} oldIndex
66911 * @param {Number} newIndex
66913 "columnmove" : true,
66916 * Fires when row(s) start being dragged
66917 * @param {Grid} this
66918 * @param {Roo.GridDD} dd The drag drop object
66919 * @param {event} e The raw browser event
66921 "startdrag" : true,
66924 * Fires when a drag operation is complete
66925 * @param {Grid} this
66926 * @param {Roo.GridDD} dd The drag drop object
66927 * @param {event} e The raw browser event
66932 * Fires when dragged row(s) are dropped on a valid DD target
66933 * @param {Grid} this
66934 * @param {Roo.GridDD} dd The drag drop object
66935 * @param {String} targetId The target drag drop object
66936 * @param {event} e The raw browser event
66941 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
66942 * @param {Grid} this
66943 * @param {Roo.GridDD} dd The drag drop object
66944 * @param {String} targetId The target drag drop object
66945 * @param {event} e The raw browser event
66950 * Fires when the dragged row(s) first cross another DD target while being dragged
66951 * @param {Grid} this
66952 * @param {Roo.GridDD} dd The drag drop object
66953 * @param {String} targetId The target drag drop object
66954 * @param {event} e The raw browser event
66956 "dragenter" : true,
66959 * Fires when the dragged row(s) leave another DD target while being dragged
66960 * @param {Grid} this
66961 * @param {Roo.GridDD} dd The drag drop object
66962 * @param {String} targetId The target drag drop object
66963 * @param {event} e The raw browser event
66968 * Fires when a row is rendered, so you can change add a style to it.
66969 * @param {GridView} gridview The grid view
66970 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
66976 * Fires when the grid is rendered
66977 * @param {Grid} grid
66982 * Fires when a date is selected
66983 * @param {DatePicker} this
66984 * @param {Date} date The selected date
66988 * @event monthchange
66989 * Fires when the displayed month changes
66990 * @param {DatePicker} this
66991 * @param {Date} date The selected month
66993 'monthchange': true,
66995 * @event evententer
66996 * Fires when mouse over an event
66997 * @param {Calendar} this
66998 * @param {event} Event
67000 'evententer': true,
67002 * @event eventleave
67003 * Fires when the mouse leaves an
67004 * @param {Calendar} this
67007 'eventleave': true,
67009 * @event eventclick
67010 * Fires when the mouse click an
67011 * @param {Calendar} this
67014 'eventclick': true,
67016 * @event eventrender
67017 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
67018 * @param {Calendar} this
67019 * @param {data} data to be modified
67021 'eventrender': true
67025 Roo.grid.Grid.superclass.constructor.call(this);
67026 this.on('render', function() {
67027 this.view.el.addClass('x-grid-cal');
67029 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
67033 if (!Roo.grid.Calendar.style) {
67034 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
67037 '.x-grid-cal .x-grid-col' : {
67038 height: 'auto !important',
67039 'vertical-align': 'top'
67041 '.x-grid-cal .fc-event-hori' : {
67052 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
67054 * @cfg {Store} eventStore The store that loads events.
67059 activeDate : false,
67062 monitorWindowResize : false,
67065 resizeColumns : function() {
67066 var col = (this.view.el.getWidth() / 7) - 3;
67067 // loop through cols, and setWidth
67068 for(var i =0 ; i < 7 ; i++){
67069 this.cm.setColumnWidth(i, col);
67072 setDate :function(date) {
67074 Roo.log('setDate?');
67076 this.resizeColumns();
67077 var vd = this.activeDate;
67078 this.activeDate = date;
67079 // if(vd && this.el){
67080 // var t = date.getTime();
67081 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
67082 // Roo.log('using add remove');
67084 // this.fireEvent('monthchange', this, date);
67086 // this.cells.removeClass("fc-state-highlight");
67087 // this.cells.each(function(c){
67088 // if(c.dateValue == t){
67089 // c.addClass("fc-state-highlight");
67090 // setTimeout(function(){
67091 // try{c.dom.firstChild.focus();}catch(e){}
67101 var days = date.getDaysInMonth();
67103 var firstOfMonth = date.getFirstDateOfMonth();
67104 var startingPos = firstOfMonth.getDay()-this.startDay;
67106 if(startingPos < this.startDay){
67110 var pm = date.add(Date.MONTH, -1);
67111 var prevStart = pm.getDaysInMonth()-startingPos;
67115 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
67117 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
67118 //this.cells.addClassOnOver('fc-state-hover');
67120 var cells = this.cells.elements;
67121 var textEls = this.textNodes;
67123 //Roo.each(cells, function(cell){
67124 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
67127 days += startingPos;
67129 // convert everything to numbers so it's fast
67130 var day = 86400000;
67131 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
67134 //Roo.log(prevStart);
67136 var today = new Date().clearTime().getTime();
67137 var sel = date.clearTime().getTime();
67138 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
67139 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
67140 var ddMatch = this.disabledDatesRE;
67141 var ddText = this.disabledDatesText;
67142 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
67143 var ddaysText = this.disabledDaysText;
67144 var format = this.format;
67146 var setCellClass = function(cal, cell){
67148 //Roo.log('set Cell Class');
67150 var t = d.getTime();
67155 cell.dateValue = t;
67157 cell.className += " fc-today";
67158 cell.className += " fc-state-highlight";
67159 cell.title = cal.todayText;
67162 // disable highlight in other month..
67163 cell.className += " fc-state-highlight";
67168 //cell.className = " fc-state-disabled";
67169 cell.title = cal.minText;
67173 //cell.className = " fc-state-disabled";
67174 cell.title = cal.maxText;
67178 if(ddays.indexOf(d.getDay()) != -1){
67179 // cell.title = ddaysText;
67180 // cell.className = " fc-state-disabled";
67183 if(ddMatch && format){
67184 var fvalue = d.dateFormat(format);
67185 if(ddMatch.test(fvalue)){
67186 cell.title = ddText.replace("%0", fvalue);
67187 cell.className = " fc-state-disabled";
67191 if (!cell.initialClassName) {
67192 cell.initialClassName = cell.dom.className;
67195 cell.dom.className = cell.initialClassName + ' ' + cell.className;
67200 for(; i < startingPos; i++) {
67201 cells[i].dayName = (++prevStart);
67202 Roo.log(textEls[i]);
67203 d.setDate(d.getDate()+1);
67205 //cells[i].className = "fc-past fc-other-month";
67206 setCellClass(this, cells[i]);
67211 for(; i < days; i++){
67212 intDay = i - startingPos + 1;
67213 cells[i].dayName = (intDay);
67214 d.setDate(d.getDate()+1);
67216 cells[i].className = ''; // "x-date-active";
67217 setCellClass(this, cells[i]);
67221 for(; i < 42; i++) {
67222 //textEls[i].innerHTML = (++extraDays);
67224 d.setDate(d.getDate()+1);
67225 cells[i].dayName = (++extraDays);
67226 cells[i].className = "fc-future fc-other-month";
67227 setCellClass(this, cells[i]);
67230 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
67232 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
67234 // this will cause all the cells to mis
67237 for (var r = 0;r < 6;r++) {
67238 for (var c =0;c < 7;c++) {
67239 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
67243 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
67244 for(i=0;i<cells.length;i++) {
67246 this.cells.elements[i].dayName = cells[i].dayName ;
67247 this.cells.elements[i].className = cells[i].className;
67248 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
67249 this.cells.elements[i].title = cells[i].title ;
67250 this.cells.elements[i].dateValue = cells[i].dateValue ;
67256 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
67257 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
67259 ////if(totalRows != 6){
67260 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
67261 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
67264 this.fireEvent('monthchange', this, date);
67269 * Returns the grid's SelectionModel.
67270 * @return {SelectionModel}
67272 getSelectionModel : function(){
67273 if(!this.selModel){
67274 this.selModel = new Roo.grid.CellSelectionModel();
67276 return this.selModel;
67280 this.eventStore.load()
67286 findCell : function(dt) {
67287 dt = dt.clearTime().getTime();
67289 this.cells.each(function(c){
67290 //Roo.log("check " +c.dateValue + '?=' + dt);
67291 if(c.dateValue == dt){
67301 findCells : function(rec) {
67302 var s = rec.data.start_dt.clone().clearTime().getTime();
67304 var e= rec.data.end_dt.clone().clearTime().getTime();
67307 this.cells.each(function(c){
67308 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
67310 if(c.dateValue > e){
67313 if(c.dateValue < s){
67322 findBestRow: function(cells)
67326 for (var i =0 ; i < cells.length;i++) {
67327 ret = Math.max(cells[i].rows || 0,ret);
67334 addItem : function(rec)
67336 // look for vertical location slot in
67337 var cells = this.findCells(rec);
67339 rec.row = this.findBestRow(cells);
67341 // work out the location.
67345 for(var i =0; i < cells.length; i++) {
67353 if (crow.start.getY() == cells[i].getY()) {
67355 crow.end = cells[i];
67371 for (var i = 0; i < cells.length;i++) {
67372 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
67379 clearEvents: function() {
67381 if (!this.eventStore.getCount()) {
67384 // reset number of rows in cells.
67385 Roo.each(this.cells.elements, function(c){
67389 this.eventStore.each(function(e) {
67390 this.clearEvent(e);
67395 clearEvent : function(ev)
67398 Roo.each(ev.els, function(el) {
67399 el.un('mouseenter' ,this.onEventEnter, this);
67400 el.un('mouseleave' ,this.onEventLeave, this);
67408 renderEvent : function(ev,ctr) {
67410 ctr = this.view.el.select('.fc-event-container',true).first();
67414 this.clearEvent(ev);
67420 var cells = ev.cells;
67421 var rows = ev.rows;
67422 this.fireEvent('eventrender', this, ev);
67424 for(var i =0; i < rows.length; i++) {
67428 cls += ' fc-event-start';
67430 if ((i+1) == rows.length) {
67431 cls += ' fc-event-end';
67434 //Roo.log(ev.data);
67435 // how many rows should it span..
67436 var cg = this.eventTmpl.append(ctr,Roo.apply({
67439 }, ev.data) , true);
67442 cg.on('mouseenter' ,this.onEventEnter, this, ev);
67443 cg.on('mouseleave' ,this.onEventLeave, this, ev);
67444 cg.on('click', this.onEventClick, this, ev);
67448 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
67449 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
67452 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
67453 cg.setWidth(ebox.right - sbox.x -2);
67457 renderEvents: function()
67459 // first make sure there is enough space..
67461 if (!this.eventTmpl) {
67462 this.eventTmpl = new Roo.Template(
67463 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
67464 '<div class="fc-event-inner">' +
67465 '<span class="fc-event-time">{time}</span>' +
67466 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
67468 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
67476 this.cells.each(function(c) {
67477 //Roo.log(c.select('.fc-day-content div',true).first());
67478 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
67481 var ctr = this.view.el.select('.fc-event-container',true).first();
67484 this.eventStore.each(function(ev){
67486 this.renderEvent(ev);
67490 this.view.layout();
67494 onEventEnter: function (e, el,event,d) {
67495 this.fireEvent('evententer', this, el, event);
67498 onEventLeave: function (e, el,event,d) {
67499 this.fireEvent('eventleave', this, el, event);
67502 onEventClick: function (e, el,event,d) {
67503 this.fireEvent('eventclick', this, el, event);
67506 onMonthChange: function () {
67510 onLoad: function () {
67512 //Roo.log('calendar onload');
67514 if(this.eventStore.getCount() > 0){
67518 this.eventStore.each(function(d){
67523 if (typeof(add.end_dt) == 'undefined') {
67524 Roo.log("Missing End time in calendar data: ");
67528 if (typeof(add.start_dt) == 'undefined') {
67529 Roo.log("Missing Start time in calendar data: ");
67533 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
67534 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
67535 add.id = add.id || d.id;
67536 add.title = add.title || '??';
67544 this.renderEvents();
67554 render : function ()
67558 if (!this.view.el.hasClass('course-timesheet')) {
67559 this.view.el.addClass('course-timesheet');
67561 if (this.tsStyle) {
67566 Roo.log(_this.grid.view.el.getWidth());
67569 this.tsStyle = Roo.util.CSS.createStyleSheet({
67570 '.course-timesheet .x-grid-row' : {
67573 '.x-grid-row td' : {
67574 'vertical-align' : 0
67576 '.course-edit-link' : {
67578 'text-overflow' : 'ellipsis',
67579 'overflow' : 'hidden',
67580 'white-space' : 'nowrap',
67581 'cursor' : 'pointer'
67586 '.de-act-sup-link' : {
67587 'color' : 'purple',
67588 'text-decoration' : 'line-through'
67592 'text-decoration' : 'line-through'
67594 '.course-timesheet .course-highlight' : {
67595 'border-top-style': 'dashed !important',
67596 'border-bottom-bottom': 'dashed !important'
67598 '.course-timesheet .course-item' : {
67599 'font-family' : 'tahoma, arial, helvetica',
67600 'font-size' : '11px',
67601 'overflow' : 'hidden',
67602 'padding-left' : '10px',
67603 'padding-right' : '10px',
67604 'padding-top' : '10px'
67612 monitorWindowResize : false,
67613 cellrenderer : function(v,x,r)
67618 xtype: 'CellSelectionModel',
67625 beforeload : function (_self, options)
67627 options.params = options.params || {};
67628 options.params._month = _this.monthField.getValue();
67629 options.params.limit = 9999;
67630 options.params['sort'] = 'when_dt';
67631 options.params['dir'] = 'ASC';
67632 this.proxy.loadResponse = this.loadResponse;
67634 //this.addColumns();
67636 load : function (_self, records, options)
67638 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
67639 // if you click on the translation.. you can edit it...
67640 var el = Roo.get(this);
67641 var id = el.dom.getAttribute('data-id');
67642 var d = el.dom.getAttribute('data-date');
67643 var t = el.dom.getAttribute('data-time');
67644 //var id = this.child('span').dom.textContent;
67647 Pman.Dialog.CourseCalendar.show({
67651 productitem_active : id ? 1 : 0
67653 _this.grid.ds.load({});
67658 _this.panel.fireEvent('resize', [ '', '' ]);
67661 loadResponse : function(o, success, response){
67662 // this is overridden on before load..
67664 Roo.log("our code?");
67665 //Roo.log(success);
67666 //Roo.log(response)
67667 delete this.activeRequest;
67669 this.fireEvent("loadexception", this, o, response);
67670 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67675 result = o.reader.read(response);
67677 Roo.log("load exception?");
67678 this.fireEvent("loadexception", this, o, response, e);
67679 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67682 Roo.log("ready...");
67683 // loop through result.records;
67684 // and set this.tdate[date] = [] << array of records..
67686 Roo.each(result.records, function(r){
67688 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
67689 _this.tdata[r.data.when_dt.format('j')] = [];
67691 _this.tdata[r.data.when_dt.format('j')].push(r.data);
67694 //Roo.log(_this.tdata);
67696 result.records = [];
67697 result.totalRecords = 6;
67699 // let's generate some duumy records for the rows.
67700 //var st = _this.dateField.getValue();
67702 // work out monday..
67703 //st = st.add(Date.DAY, -1 * st.format('w'));
67705 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67707 var firstOfMonth = date.getFirstDayOfMonth();
67708 var days = date.getDaysInMonth();
67710 var firstAdded = false;
67711 for (var i = 0; i < result.totalRecords ; i++) {
67712 //var d= st.add(Date.DAY, i);
67715 for(var w = 0 ; w < 7 ; w++){
67716 if(!firstAdded && firstOfMonth != w){
67723 var dd = (d > 0 && d < 10) ? "0"+d : d;
67724 row['weekday'+w] = String.format(
67725 '<span style="font-size: 16px;"><b>{0}</b></span>'+
67726 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
67728 date.format('Y-m-')+dd
67731 if(typeof(_this.tdata[d]) != 'undefined'){
67732 Roo.each(_this.tdata[d], function(r){
67736 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
67737 if(r.parent_id*1>0){
67738 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
67741 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
67742 deactive = 'de-act-link';
67745 row['weekday'+w] += String.format(
67746 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
67748 r.product_id_name, //1
67749 r.when_dt.format('h:ia'), //2
67759 // only do this if something added..
67761 result.records.push(_this.grid.dataSource.reader.newRow(row));
67765 // push it twice. (second one with an hour..
67769 this.fireEvent("load", this, o, o.request.arg);
67770 o.request.callback.call(o.request.scope, result, o.request.arg, true);
67772 sortInfo : {field: 'when_dt', direction : 'ASC' },
67774 xtype: 'HttpProxy',
67777 url : baseURL + '/Roo/Shop_course.php'
67780 xtype: 'JsonReader',
67797 'name': 'parent_id',
67801 'name': 'product_id',
67805 'name': 'productitem_id',
67823 click : function (_self, e)
67825 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67826 sd.setMonth(sd.getMonth()-1);
67827 _this.monthField.setValue(sd.format('Y-m-d'));
67828 _this.grid.ds.load({});
67834 xtype: 'Separator',
67838 xtype: 'MonthField',
67841 render : function (_self)
67843 _this.monthField = _self;
67844 // _this.monthField.set today
67846 select : function (combo, date)
67848 _this.grid.ds.load({});
67851 value : (function() { return new Date(); })()
67854 xtype: 'Separator',
67860 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
67870 click : function (_self, e)
67872 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67873 sd.setMonth(sd.getMonth()+1);
67874 _this.monthField.setValue(sd.format('Y-m-d'));
67875 _this.grid.ds.load({});
67888 * Ext JS Library 1.1.1
67889 * Copyright(c) 2006-2007, Ext JS, LLC.
67891 * Originally Released Under LGPL - original licence link has changed is not relivant.
67894 * <script type="text/javascript">
67898 * @class Roo.LoadMask
67899 * A simple utility class for generically masking elements while loading data. If the element being masked has
67900 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
67901 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
67902 * element's UpdateManager load indicator and will be destroyed after the initial load.
67904 * Create a new LoadMask
67905 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
67906 * @param {Object} config The config object
67908 Roo.LoadMask = function(el, config){
67909 this.el = Roo.get(el);
67910 Roo.apply(this, config);
67912 this.store.on('beforeload', this.onBeforeLoad, this);
67913 this.store.on('load', this.onLoad, this);
67914 this.store.on('loadexception', this.onLoadException, this);
67915 this.removeMask = false;
67917 var um = this.el.getUpdateManager();
67918 um.showLoadIndicator = false; // disable the default indicator
67919 um.on('beforeupdate', this.onBeforeLoad, this);
67920 um.on('update', this.onLoad, this);
67921 um.on('failure', this.onLoad, this);
67922 this.removeMask = true;
67926 Roo.LoadMask.prototype = {
67928 * @cfg {Boolean} removeMask
67929 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
67930 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
67932 removeMask : false,
67934 * @cfg {String} msg
67935 * The text to display in a centered loading message box (defaults to 'Loading...')
67937 msg : 'Loading...',
67939 * @cfg {String} msgCls
67940 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
67942 msgCls : 'x-mask-loading',
67945 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
67951 * Disables the mask to prevent it from being displayed
67953 disable : function(){
67954 this.disabled = true;
67958 * Enables the mask so that it can be displayed
67960 enable : function(){
67961 this.disabled = false;
67964 onLoadException : function()
67966 Roo.log(arguments);
67968 if (typeof(arguments[3]) != 'undefined') {
67969 Roo.MessageBox.alert("Error loading",arguments[3]);
67973 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
67974 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
67981 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67984 onLoad : function()
67986 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67990 onBeforeLoad : function(){
67991 if(!this.disabled){
67992 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
67997 destroy : function(){
67999 this.store.un('beforeload', this.onBeforeLoad, this);
68000 this.store.un('load', this.onLoad, this);
68001 this.store.un('loadexception', this.onLoadException, this);
68003 var um = this.el.getUpdateManager();
68004 um.un('beforeupdate', this.onBeforeLoad, this);
68005 um.un('update', this.onLoad, this);
68006 um.un('failure', this.onLoad, this);
68011 * Ext JS Library 1.1.1
68012 * Copyright(c) 2006-2007, Ext JS, LLC.
68014 * Originally Released Under LGPL - original licence link has changed is not relivant.
68017 * <script type="text/javascript">
68022 * @class Roo.XTemplate
68023 * @extends Roo.Template
68024 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
68026 var t = new Roo.XTemplate(
68027 '<select name="{name}">',
68028 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
68032 // then append, applying the master template values
68035 * Supported features:
68040 {a_variable} - output encoded.
68041 {a_variable.format:("Y-m-d")} - call a method on the variable
68042 {a_variable:raw} - unencoded output
68043 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
68044 {a_variable:this.method_on_template(...)} - call a method on the template object.
68049 <tpl for="a_variable or condition.."></tpl>
68050 <tpl if="a_variable or condition"></tpl>
68051 <tpl exec="some javascript"></tpl>
68052 <tpl name="named_template"></tpl> (experimental)
68054 <tpl for="."></tpl> - just iterate the property..
68055 <tpl for=".."></tpl> - iterates with the parent (probably the template)
68059 Roo.XTemplate = function()
68061 Roo.XTemplate.superclass.constructor.apply(this, arguments);
68068 Roo.extend(Roo.XTemplate, Roo.Template, {
68071 * The various sub templates
68076 * basic tag replacing syntax
68079 * // you can fake an object call by doing this
68083 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
68086 * compile the template
68088 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
68091 compile: function()
68095 s = ['<tpl>', s, '</tpl>'].join('');
68097 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
68098 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
68099 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
68100 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
68101 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
68106 while(true == !!(m = s.match(re))){
68107 var forMatch = m[0].match(nameRe),
68108 ifMatch = m[0].match(ifRe),
68109 execMatch = m[0].match(execRe),
68110 namedMatch = m[0].match(namedRe),
68115 name = forMatch && forMatch[1] ? forMatch[1] : '';
68118 // if - puts fn into test..
68119 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
68121 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
68126 // exec - calls a function... returns empty if true is returned.
68127 exp = execMatch && execMatch[1] ? execMatch[1] : null;
68129 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
68137 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
68138 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
68139 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
68142 var uid = namedMatch ? namedMatch[1] : id;
68146 id: namedMatch ? namedMatch[1] : id,
68153 s = s.replace(m[0], '');
68155 s = s.replace(m[0], '{xtpl'+ id + '}');
68160 for(var i = tpls.length-1; i >= 0; --i){
68161 this.compileTpl(tpls[i]);
68162 this.tpls[tpls[i].id] = tpls[i];
68164 this.master = tpls[tpls.length-1];
68168 * same as applyTemplate, except it's done to one of the subTemplates
68169 * when using named templates, you can do:
68171 * var str = pl.applySubTemplate('your-name', values);
68174 * @param {Number} id of the template
68175 * @param {Object} values to apply to template
68176 * @param {Object} parent (normaly the instance of this object)
68178 applySubTemplate : function(id, values, parent)
68182 var t = this.tpls[id];
68186 if(t.test && !t.test.call(this, values, parent)){
68190 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
68191 Roo.log(e.toString());
68197 if(t.exec && t.exec.call(this, values, parent)){
68201 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
68202 Roo.log(e.toString());
68207 var vs = t.target ? t.target.call(this, values, parent) : values;
68208 parent = t.target ? values : parent;
68209 if(t.target && vs instanceof Array){
68211 for(var i = 0, len = vs.length; i < len; i++){
68212 buf[buf.length] = t.compiled.call(this, vs[i], parent);
68214 return buf.join('');
68216 return t.compiled.call(this, vs, parent);
68218 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
68219 Roo.log(e.toString());
68220 Roo.log(t.compiled);
68225 compileTpl : function(tpl)
68227 var fm = Roo.util.Format;
68228 var useF = this.disableFormats !== true;
68229 var sep = Roo.isGecko ? "+" : ",";
68230 var undef = function(str) {
68231 Roo.log("Property not found :" + str);
68235 var fn = function(m, name, format, args)
68237 //Roo.log(arguments);
68238 args = args ? args.replace(/\\'/g,"'") : args;
68239 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
68240 if (typeof(format) == 'undefined') {
68241 format= 'htmlEncode';
68243 if (format == 'raw' ) {
68247 if(name.substr(0, 4) == 'xtpl'){
68248 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
68251 // build an array of options to determine if value is undefined..
68253 // basically get 'xxxx.yyyy' then do
68254 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
68255 // (function () { Roo.log("Property not found"); return ''; })() :
68260 Roo.each(name.split('.'), function(st) {
68261 lookfor += (lookfor.length ? '.': '') + st;
68262 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
68265 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
68268 if(format && useF){
68270 args = args ? ',' + args : "";
68272 if(format.substr(0, 5) != "this."){
68273 format = "fm." + format + '(';
68275 format = 'this.call("'+ format.substr(5) + '", ';
68279 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
68283 // called with xxyx.yuu:(test,test)
68285 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
68287 // raw.. - :raw modifier..
68288 return "'"+ sep + udef_st + name + ")"+sep+"'";
68292 // branched to use + in gecko and [].join() in others
68294 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
68295 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
68298 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
68299 body.push(tpl.body.replace(/(\r\n|\n)/g,
68300 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
68301 body.push("'].join('');};};");
68302 body = body.join('');
68305 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
68307 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
68313 applyTemplate : function(values){
68314 return this.master.compiled.call(this, values, {});
68315 //var s = this.subs;
68318 apply : function(){
68319 return this.applyTemplate.apply(this, arguments);
68324 Roo.XTemplate.from = function(el){
68325 el = Roo.getDom(el);
68326 return new Roo.XTemplate(el.value || el.innerHTML);
68333 * @class Roo.dialog.UploadCropbox
68334 * @extends Roo.BoxComponent
68335 * Dialog UploadCropbox class
68336 * @cfg {String} emptyText show when image has been loaded
68337 * @cfg {String} rotateNotify show when image too small to rotate
68338 * @cfg {Number} errorTimeout default 3000
68339 * @cfg {Number} minWidth default 300
68340 * @cfg {Number} minHeight default 300
68341 * @cfg {Number} outputMaxWidth default 1200
68342 * @cfg {Number} windowSize default 300
68343 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
68344 * @cfg {Boolean} isDocument (true|false) default false
68345 * @cfg {String} url action url
68346 * @cfg {String} paramName default 'imageUpload'
68347 * @cfg {String} method default POST
68348 * @cfg {Boolean} loadMask (true|false) default true
68349 * @cfg {Boolean} loadingText default 'Loading...'
68352 * Create a new UploadCropbox
68353 * @param {Object} config The config object
68356 Roo.dialog.UploadCropbox = function(config){
68357 Roo.dialog.UploadCropbox.superclass.constructor.call(this, config);
68361 * @event beforeselectfile
68362 * Fire before select file
68363 * @param {Roo.dialog.UploadCropbox} this
68365 "beforeselectfile" : true,
68368 * Fire after initEvent
68369 * @param {Roo.dialog.UploadCropbox} this
68374 * Fire after initEvent
68375 * @param {Roo.dialog.UploadCropbox} this
68376 * @param {String} data
68381 * Fire when preparing the file data
68382 * @param {Roo.dialog.UploadCropbox} this
68383 * @param {Object} file
68388 * Fire when get exception
68389 * @param {Roo.dialog.UploadCropbox} this
68390 * @param {XMLHttpRequest} xhr
68392 "exception" : true,
68394 * @event beforeloadcanvas
68395 * Fire before load the canvas
68396 * @param {Roo.dialog.UploadCropbox} this
68397 * @param {String} src
68399 "beforeloadcanvas" : true,
68402 * Fire when trash image
68403 * @param {Roo.dialog.UploadCropbox} this
68408 * Fire when download the image
68409 * @param {Roo.dialog.UploadCropbox} this
68413 * @event footerbuttonclick
68414 * Fire when footerbuttonclick
68415 * @param {Roo.dialog.UploadCropbox} this
68416 * @param {String} type
68418 "footerbuttonclick" : true,
68422 * @param {Roo.dialog.UploadCropbox} this
68427 * Fire when rotate the image
68428 * @param {Roo.dialog.UploadCropbox} this
68429 * @param {String} pos
68434 * Fire when inspect the file
68435 * @param {Roo.dialog.UploadCropbox} this
68436 * @param {Object} file
68441 * Fire when xhr upload the file
68442 * @param {Roo.dialog.UploadCropbox} this
68443 * @param {Object} data
68448 * Fire when arrange the file data
68449 * @param {Roo.dialog.UploadCropbox} this
68450 * @param {Object} formData
68454 * @event loadcanvas
68455 * Fire after load the canvas
68456 * @param {Roo.dialog.UploadCropbox}
68457 * @param {Object} imgEl
68459 "loadcanvas" : true
68462 this.buttons = this.buttons || Roo.dialog.UploadCropbox.footer.STANDARD;
68465 Roo.extend(Roo.dialog.UploadCropbox, Roo.Component, {
68467 emptyText : 'Click to upload image',
68468 rotateNotify : 'Image is too small to rotate',
68469 errorTimeout : 3000,
68480 outputMaxWidth : 1200,
68485 cropType : 'image/jpeg',
68487 canvasLoaded : false,
68488 isDocument : false,
68490 paramName : 'imageUpload',
68492 loadingText : 'Loading...',
68495 getAutoCreate : function()
68499 cls : 'roo-upload-cropbox',
68503 cls : 'roo-upload-cropbox-selector',
68508 cls : 'roo-upload-cropbox-body',
68509 style : 'cursor:pointer',
68513 cls : 'roo-upload-cropbox-preview'
68517 cls : 'roo-upload-cropbox-thumb'
68521 cls : 'roo-upload-cropbox-empty-notify',
68522 html : this.emptyText
68526 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
68527 html : this.rotateNotify
68533 cls : 'roo-upload-cropbox-footer',
68536 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
68546 onRender : function(ct, position)
68548 Roo.dialog.UploadCropbox.superclass.onRender.call(this, ct, position);
68551 if (this.el.attr('xtype')) {
68552 this.el.attr('xtypex', this.el.attr('xtype'));
68553 this.el.dom.removeAttribute('xtype');
68559 var cfg = Roo.apply({}, this.getAutoCreate());
68561 cfg.id = this.id || Roo.id();
68564 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
68567 if (this.style) { // fixme needs to support more complex style data.
68568 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
68571 this.el = ct.createChild(cfg, position);
68576 if (this.buttons.length) {
68578 Roo.each(this.buttons, function(bb) {
68580 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
68582 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
68588 this.maskEl = this.el;
68592 initEvents : function()
68594 this.urlAPI = (window.createObjectURL && window) ||
68595 (window.URL && URL.revokeObjectURL && URL) ||
68596 (window.webkitURL && webkitURL);
68598 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
68599 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68601 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
68602 this.selectorEl.hide();
68604 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
68605 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68607 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
68608 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68609 this.thumbEl.hide();
68611 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
68612 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68614 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
68615 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68616 this.errorEl.hide();
68618 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
68619 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68620 this.footerEl.hide();
68622 this.setThumbBoxSize();
68628 this.fireEvent('initial', this);
68635 window.addEventListener("resize", function() { _this.resize(); } );
68637 this.bodyEl.on('click', this.beforeSelectFile, this);
68640 this.bodyEl.on('touchstart', this.onTouchStart, this);
68641 this.bodyEl.on('touchmove', this.onTouchMove, this);
68642 this.bodyEl.on('touchend', this.onTouchEnd, this);
68646 this.bodyEl.on('mousedown', this.onMouseDown, this);
68647 this.bodyEl.on('mousemove', this.onMouseMove, this);
68648 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
68649 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
68650 Roo.get(document).on('mouseup', this.onMouseUp, this);
68653 this.selectorEl.on('change', this.onFileSelected, this);
68659 this.baseScale = 1;
68661 this.baseRotate = 1;
68662 this.dragable = false;
68663 this.pinching = false;
68666 this.cropData = false;
68667 this.notifyEl.dom.innerHTML = this.emptyText;
68669 // this.selectorEl.dom.value = '';
68673 resize : function()
68675 if(this.fireEvent('resize', this) != false){
68676 this.setThumbBoxPosition();
68677 this.setCanvasPosition();
68681 onFooterButtonClick : function(e, el, o, type)
68684 case 'rotate-left' :
68685 this.onRotateLeft(e);
68687 case 'rotate-right' :
68688 this.onRotateRight(e);
68691 this.beforeSelectFile(e);
68709 this.fireEvent('footerbuttonclick', this, type);
68712 beforeSelectFile : function(e)
68714 e.preventDefault();
68716 if(this.fireEvent('beforeselectfile', this) != false){
68717 this.selectorEl.dom.click();
68721 onFileSelected : function(e)
68723 e.preventDefault();
68725 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
68729 var file = this.selectorEl.dom.files[0];
68731 if(this.fireEvent('inspect', this, file) != false){
68732 this.prepare(file);
68737 trash : function(e)
68739 this.fireEvent('trash', this);
68742 download : function(e)
68744 this.fireEvent('download', this);
68747 center : function(e)
68749 this.setCanvasPosition();
68752 loadCanvas : function(src)
68754 if(this.fireEvent('beforeloadcanvas', this, src) != false){
68758 this.imageEl = document.createElement('img');
68762 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
68764 this.imageEl.src = src;
68768 onLoadCanvas : function()
68770 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
68771 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
68773 if(this.fireEvent('loadcanvas', this, this.imageEl) != false){
68775 this.bodyEl.un('click', this.beforeSelectFile, this);
68777 this.notifyEl.hide();
68778 this.thumbEl.show();
68779 this.footerEl.show();
68781 this.baseRotateLevel();
68783 if(this.isDocument){
68784 this.setThumbBoxSize();
68787 this.setThumbBoxPosition();
68789 this.baseScaleLevel();
68795 this.canvasLoaded = true;
68800 this.maskEl.unmask();
68805 setCanvasPosition : function(center = true)
68807 if(!this.canvasEl){
68811 var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
68812 var newCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
68815 this.previewEl.setLeft(newCenterLeft);
68816 this.previewEl.setTop(newCenterTop);
68821 var oldScaleLevel = this.baseScale * Math.pow(1.02, this.startScale);
68822 var oldCanvasWidth = Math.floor(this.imageEl.OriginWidth * oldScaleLevel);
68823 var oldCanvasHeight = Math.floor(this.imageEl.OriginHeight * oldScaleLevel);
68825 var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - oldCanvasWidth) / 2);
68826 var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - oldCanvasHeight) / 2);
68828 var leftDiff = newCenterLeft - oldCenterLeft;
68829 var topDiff = newCenterTop - oldCenterTop;
68831 var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
68832 var newPreviewTop = this.previewEl.getTop(true) + topDiff;
68834 this.previewEl.setLeft(newPreviewLeft);
68835 this.previewEl.setTop(newPreviewTop);
68839 onMouseDown : function(e)
68843 this.dragable = true;
68844 this.pinching = false;
68846 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
68847 this.dragable = false;
68851 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
68852 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
68856 onMouseMove : function(e)
68860 if(!this.canvasLoaded){
68864 if (!this.dragable){
68868 var maxPaddingLeft = this.canvasEl.width / 0.9 * 0.05;
68869 var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
68871 if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
68872 maxPaddingLeft = (this.canvasEl.height * this.minWidth / this.minHeight - this.canvasEl.width) / 2 + maxPaddingLeft;
68875 if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
68876 maxPaddingTop = (this.canvasEl.width * this.minHeight / this.minWidth - this.canvasEl.height) / 2 + maxPaddingTop;
68879 var minX = Math.ceil(this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - this.canvasEl.width - maxPaddingLeft);
68880 var minY = Math.ceil(this.thumbEl.getTop(true) + this.thumbEl.getHeight() - this.canvasEl.height - maxPaddingTop);
68882 var maxX = Math.ceil(this.thumbEl.getLeft(true) + maxPaddingLeft);
68883 var maxY = Math.ceil(this.thumbEl.getTop(true) + maxPaddingTop);
68897 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
68898 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
68900 x = x - this.mouseX;
68901 y = y - this.mouseY;
68903 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
68904 var bgY = Math.ceil(y + this.previewEl.getTop(true));
68906 bgX = (bgX < minX) ? minX : ((bgX > maxX) ? maxX : bgX);
68907 bgY = (bgY < minY) ? minY : ((bgY > maxY) ? maxY : bgY);
68909 this.previewEl.setLeft(bgX);
68910 this.previewEl.setTop(bgY);
68912 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
68913 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
68916 onMouseUp : function(e)
68920 this.dragable = false;
68923 onMouseWheel : function(e)
68927 this.startScale = this.scale;
68928 this.scale = (e.getWheelDelta() > 0) ? (this.scale + 1) : (this.scale - 1);
68930 if(!this.zoomable()){
68931 this.scale = this.startScale;
68941 zoomable : function()
68943 var minScale = this.thumbEl.getWidth() / this.minWidth;
68945 if(this.minWidth < this.minHeight){
68946 minScale = this.thumbEl.getHeight() / this.minHeight;
68949 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
68950 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
68952 var maxWidth = this.imageEl.OriginWidth;
68953 var maxHeight = this.imageEl.OriginHeight;
68956 var newCanvasWidth = Math.floor(this.imageEl.OriginWidth * this.getScaleLevel());
68957 var newCanvasHeight = Math.floor(this.imageEl.OriginHeight * this.getScaleLevel());
68959 var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
68960 var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
68962 var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - newCanvasWidth) / 2);
68963 var newCenterTop = Math.ceil((this.bodyEl.getHeight() - newCanvasHeight) / 2);
68965 var leftDiff = newCenterLeft - oldCenterLeft;
68966 var topDiff = newCenterTop - oldCenterTop;
68968 var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
68969 var newPreviewTop = this.previewEl.getTop(true) + topDiff;
68971 var paddingLeft = newPreviewLeft - this.thumbEl.getLeft(true);
68972 var paddingTop = newPreviewTop - this.thumbEl.getTop(true);
68974 var paddingRight = this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - newCanvasWidth - newPreviewLeft;
68975 var paddingBottom = this.thumbEl.getTop(true) + this.thumbEl.getHeight() - newCanvasHeight - newPreviewTop;
68977 var maxPaddingLeft = newCanvasWidth / 0.9 * 0.05;
68978 var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
68980 if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
68981 maxPaddingLeft = (newCanvasHeight * this.minWidth / this.minHeight - newCanvasWidth) / 2 + maxPaddingLeft;
68984 if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
68985 maxPaddingTop = (newCanvasWidth * this.minHeight / this.minWidth - newCanvasHeight) / 2 + maxPaddingTop;
68990 (this.rotate == 0 || this.rotate == 180) &&
68992 width > this.imageEl.OriginWidth ||
68993 height > this.imageEl.OriginHeight ||
68994 (width < this.minWidth && height < this.minHeight)
69002 (this.rotate == 90 || this.rotate == 270) &&
69004 width > this.imageEl.OriginWidth ||
69005 height > this.imageEl.OriginHeight ||
69006 (width < this.minHeight && height < this.minWidth)
69013 !this.isDocument &&
69014 (this.rotate == 0 || this.rotate == 180) &&
69017 paddingLeft > maxPaddingLeft ||
69018 paddingRight > maxPaddingLeft ||
69019 paddingTop > maxPaddingTop ||
69020 paddingBottom > maxPaddingTop ||
69022 width > maxWidth ||
69030 !this.isDocument &&
69031 (this.rotate == 90 || this.rotate == 270) &&
69033 width < this.minHeight ||
69034 width > this.imageEl.OriginWidth ||
69035 height < this.minWidth ||
69036 height > this.imageEl.OriginHeight
69046 onRotateLeft : function(e)
69048 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
69050 var minScale = this.thumbEl.getWidth() / this.minWidth;
69052 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
69053 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
69055 this.startScale = this.scale;
69057 while (this.getScaleLevel() < minScale){
69059 this.scale = this.scale + 1;
69061 if(!this.zoomable()){
69066 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
69067 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
69072 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
69079 this.scale = this.startScale;
69081 this.onRotateFail();
69086 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
69088 if(this.isDocument){
69089 this.setThumbBoxSize();
69090 this.setThumbBoxPosition();
69091 this.setCanvasPosition();
69096 this.fireEvent('rotate', this, 'left');
69100 onRotateRight : function(e)
69102 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
69104 var minScale = this.thumbEl.getWidth() / this.minWidth;
69106 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
69107 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
69109 this.startScale = this.scale;
69111 while (this.getScaleLevel() < minScale){
69113 this.scale = this.scale + 1;
69115 if(!this.zoomable()){
69120 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
69121 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
69126 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
69133 this.scale = this.startScale;
69135 this.onRotateFail();
69140 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
69142 if(this.isDocument){
69143 this.setThumbBoxSize();
69144 this.setThumbBoxPosition();
69145 this.setCanvasPosition();
69150 this.fireEvent('rotate', this, 'right');
69153 onRotateFail : function()
69155 this.errorEl.show(true);
69159 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
69164 this.previewEl.dom.innerHTML = '';
69166 var canvasEl = document.createElement("canvas");
69168 var contextEl = canvasEl.getContext("2d");
69170 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69171 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69172 var center = this.imageEl.OriginWidth / 2;
69174 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
69175 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69176 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69177 center = this.imageEl.OriginHeight / 2;
69180 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
69182 contextEl.translate(center, center);
69183 contextEl.rotate(this.rotate * Math.PI / 180);
69185 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
69187 this.canvasEl = document.createElement("canvas");
69189 this.contextEl = this.canvasEl.getContext("2d");
69191 switch (this.rotate) {
69194 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69195 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69197 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69202 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69203 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69205 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69206 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);
69210 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69215 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69216 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69218 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69219 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);
69223 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);
69228 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69229 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69231 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69232 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69236 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);
69243 this.previewEl.appendChild(this.canvasEl);
69245 this.setCanvasPosition(false);
69250 if(!this.canvasLoaded){
69254 var imageCanvas = document.createElement("canvas");
69256 var imageContext = imageCanvas.getContext("2d");
69258 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
69259 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
69261 var center = imageCanvas.width / 2;
69263 imageContext.translate(center, center);
69265 imageContext.rotate(this.rotate * Math.PI / 180);
69267 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
69269 var canvas = document.createElement("canvas");
69271 var context = canvas.getContext("2d");
69273 canvas.width = this.thumbEl.getWidth() / this.getScaleLevel();
69275 canvas.height = this.thumbEl.getHeight() / this.getScaleLevel();
69277 switch (this.rotate) {
69280 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
69281 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
69283 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69284 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69286 var sx = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
69287 var sy = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
69289 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69290 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69292 if(canvas.width > this.outputMaxWidth) {
69293 var scale = this.outputMaxWidth / canvas.width;
69294 canvas.width = canvas.width * scale;
69295 canvas.height = canvas.height * scale;
69296 context.scale(scale, scale);
69299 context.fillStyle = 'white';
69300 context.fillRect(0, 0, this.thumbEl.getWidth() / this.getScaleLevel(), this.thumbEl.getHeight() / this.getScaleLevel());
69303 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69308 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
69309 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
69311 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69312 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69314 var targetWidth = this.minWidth - 2 * x;
69315 var targetHeight = this.minHeight - 2 * y;
69319 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69320 scale = targetWidth / width;
69323 if(x > 0 && y == 0){
69324 scale = targetHeight / height;
69327 if(x > 0 && y > 0){
69328 scale = targetWidth / width;
69330 if(width < height){
69331 scale = targetHeight / height;
69335 context.scale(scale, scale);
69337 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69338 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69340 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69341 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69343 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
69345 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69350 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
69351 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
69353 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69354 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69356 var targetWidth = this.minWidth - 2 * x;
69357 var targetHeight = this.minHeight - 2 * y;
69361 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69362 scale = targetWidth / width;
69365 if(x > 0 && y == 0){
69366 scale = targetHeight / height;
69369 if(x > 0 && y > 0){
69370 scale = targetWidth / width;
69372 if(width < height){
69373 scale = targetHeight / height;
69377 context.scale(scale, scale);
69379 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69380 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69382 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69383 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69385 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
69386 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
69388 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69393 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
69394 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
69396 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69397 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69399 var targetWidth = this.minWidth - 2 * x;
69400 var targetHeight = this.minHeight - 2 * y;
69404 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69405 scale = targetWidth / width;
69408 if(x > 0 && y == 0){
69409 scale = targetHeight / height;
69412 if(x > 0 && y > 0){
69413 scale = targetWidth / width;
69415 if(width < height){
69416 scale = targetHeight / height;
69420 context.scale(scale, scale);
69421 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69422 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69424 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69425 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69427 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
69429 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69436 this.cropData = canvas.toDataURL(this.cropType);
69438 if(this.fireEvent('crop', this, this.cropData) !== false){
69439 this.process(this.file, this.cropData);
69446 setThumbBoxSize : function()
69450 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
69451 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
69452 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
69454 this.minWidth = width;
69455 this.minHeight = height;
69457 if(this.rotate == 90 || this.rotate == 270){
69458 this.minWidth = height;
69459 this.minHeight = width;
69463 height = this.windowSize;
69464 width = Math.ceil(this.minWidth * height / this.minHeight);
69466 if(this.minWidth > this.minHeight){
69467 width = this.windowSize;
69468 height = Math.ceil(this.minHeight * width / this.minWidth);
69471 this.thumbEl.setStyle({
69472 width : width + 'px',
69473 height : height + 'px'
69480 setThumbBoxPosition : function()
69482 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
69483 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
69485 this.thumbEl.setLeft(x);
69486 this.thumbEl.setTop(y);
69490 baseRotateLevel : function()
69492 this.baseRotate = 1;
69495 typeof(this.exif) != 'undefined' &&
69496 typeof(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
69497 [1, 3, 6, 8].indexOf(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != -1
69499 this.baseRotate = this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']];
69502 this.rotate = Roo.dialog.UploadCropbox['Orientation'][this.baseRotate];
69506 baseScaleLevel : function()
69510 if(this.isDocument){
69512 if(this.baseRotate == 6 || this.baseRotate == 8){
69514 height = this.thumbEl.getHeight();
69515 this.baseScale = height / this.imageEl.OriginWidth;
69517 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
69518 width = this.thumbEl.getWidth();
69519 this.baseScale = width / this.imageEl.OriginHeight;
69525 height = this.thumbEl.getHeight();
69526 this.baseScale = height / this.imageEl.OriginHeight;
69528 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
69529 width = this.thumbEl.getWidth();
69530 this.baseScale = width / this.imageEl.OriginWidth;
69536 if(this.baseRotate == 6 || this.baseRotate == 8){
69538 width = this.thumbEl.getHeight();
69539 this.baseScale = width / this.imageEl.OriginHeight;
69541 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
69542 height = this.thumbEl.getWidth();
69543 this.baseScale = height / this.imageEl.OriginHeight;
69546 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69547 height = this.thumbEl.getWidth();
69548 this.baseScale = height / this.imageEl.OriginHeight;
69550 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
69551 width = this.thumbEl.getHeight();
69552 this.baseScale = width / this.imageEl.OriginWidth;
69559 width = this.thumbEl.getWidth();
69560 this.baseScale = width / this.imageEl.OriginWidth;
69562 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
69563 height = this.thumbEl.getHeight();
69564 this.baseScale = height / this.imageEl.OriginHeight;
69567 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69569 height = this.thumbEl.getHeight();
69570 this.baseScale = height / this.imageEl.OriginHeight;
69572 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
69573 width = this.thumbEl.getWidth();
69574 this.baseScale = width / this.imageEl.OriginWidth;
69579 if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight) {
69580 this.baseScale = width / this.minWidth;
69586 getScaleLevel : function()
69588 return this.baseScale * Math.pow(1.02, this.scale);
69591 onTouchStart : function(e)
69593 if(!this.canvasLoaded){
69594 this.beforeSelectFile(e);
69598 var touches = e.browserEvent.touches;
69604 if(touches.length == 1){
69605 this.onMouseDown(e);
69609 if(touches.length != 2){
69615 for(var i = 0, finger; finger = touches[i]; i++){
69616 coords.push(finger.pageX, finger.pageY);
69619 var x = Math.pow(coords[0] - coords[2], 2);
69620 var y = Math.pow(coords[1] - coords[3], 2);
69622 this.startDistance = Math.sqrt(x + y);
69624 this.startScale = this.scale;
69626 this.pinching = true;
69627 this.dragable = false;
69631 onTouchMove : function(e)
69633 if(!this.pinching && !this.dragable){
69637 var touches = e.browserEvent.touches;
69644 this.onMouseMove(e);
69650 for(var i = 0, finger; finger = touches[i]; i++){
69651 coords.push(finger.pageX, finger.pageY);
69654 var x = Math.pow(coords[0] - coords[2], 2);
69655 var y = Math.pow(coords[1] - coords[3], 2);
69657 this.endDistance = Math.sqrt(x + y);
69659 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
69661 if(!this.zoomable()){
69662 this.scale = this.startScale;
69670 onTouchEnd : function(e)
69672 this.pinching = false;
69673 this.dragable = false;
69677 process : function(file, crop)
69680 this.maskEl.mask(this.loadingText);
69683 this.xhr = new XMLHttpRequest();
69685 file.xhr = this.xhr;
69687 this.xhr.open(this.method, this.url, true);
69690 "Accept": "application/json",
69691 "Cache-Control": "no-cache",
69692 "X-Requested-With": "XMLHttpRequest"
69695 for (var headerName in headers) {
69696 var headerValue = headers[headerName];
69698 this.xhr.setRequestHeader(headerName, headerValue);
69704 this.xhr.onload = function()
69706 _this.xhrOnLoad(_this.xhr);
69709 this.xhr.onerror = function()
69711 _this.xhrOnError(_this.xhr);
69714 var formData = new FormData();
69716 formData.append('returnHTML', 'NO');
69719 formData.append('crop', crop);
69720 var blobBin = atob(crop.split(',')[1]);
69722 for(var i = 0; i < blobBin.length; i++) {
69723 array.push(blobBin.charCodeAt(i));
69725 var croppedFile =new Blob([new Uint8Array(array)], {type: this.cropType});
69726 formData.append(this.paramName, croppedFile, file.name);
69729 if(typeof(file.filename) != 'undefined'){
69730 formData.append('filename', file.filename);
69733 if(typeof(file.mimetype) != 'undefined'){
69734 formData.append('mimetype', file.mimetype);
69737 if(this.fireEvent('arrange', this, formData) != false){
69738 this.xhr.send(formData);
69742 xhrOnLoad : function(xhr)
69745 this.maskEl.unmask();
69748 if (xhr.readyState !== 4) {
69749 this.fireEvent('exception', this, xhr);
69753 var response = Roo.decode(xhr.responseText);
69755 if(!response.success){
69756 this.fireEvent('exception', this, xhr);
69760 var response = Roo.decode(xhr.responseText);
69762 this.fireEvent('upload', this, response);
69766 xhrOnError : function()
69769 this.maskEl.unmask();
69772 Roo.log('xhr on error');
69774 var response = Roo.decode(xhr.responseText);
69780 prepare : function(file)
69783 this.maskEl.mask(this.loadingText);
69789 if(typeof(file) === 'string'){
69790 this.loadCanvas(file);
69794 if(!file || !this.urlAPI){
69799 if(typeof(file.type) != 'undefined' && file.type.length != 0) {
69800 this.cropType = file.type;
69805 if(this.fireEvent('prepare', this, this.file) != false){
69807 var reader = new FileReader();
69809 reader.onload = function (e) {
69810 if (e.target.error) {
69811 Roo.log(e.target.error);
69815 var buffer = e.target.result,
69816 dataView = new DataView(buffer),
69818 maxOffset = dataView.byteLength - 4,
69822 if (dataView.getUint16(0) === 0xffd8) {
69823 while (offset < maxOffset) {
69824 markerBytes = dataView.getUint16(offset);
69826 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
69827 markerLength = dataView.getUint16(offset + 2) + 2;
69828 if (offset + markerLength > dataView.byteLength) {
69829 Roo.log('Invalid meta data: Invalid segment size.');
69833 if(markerBytes == 0xffe1){
69834 _this.parseExifData(
69841 offset += markerLength;
69851 var url = _this.urlAPI.createObjectURL(_this.file);
69853 _this.loadCanvas(url);
69858 reader.readAsArrayBuffer(this.file);
69864 parseExifData : function(dataView, offset, length)
69866 var tiffOffset = offset + 10,
69870 if (dataView.getUint32(offset + 4) !== 0x45786966) {
69871 // No Exif data, might be XMP data instead
69875 // Check for the ASCII code for "Exif" (0x45786966):
69876 if (dataView.getUint32(offset + 4) !== 0x45786966) {
69877 // No Exif data, might be XMP data instead
69880 if (tiffOffset + 8 > dataView.byteLength) {
69881 Roo.log('Invalid Exif data: Invalid segment size.');
69884 // Check for the two null bytes:
69885 if (dataView.getUint16(offset + 8) !== 0x0000) {
69886 Roo.log('Invalid Exif data: Missing byte alignment offset.');
69889 // Check the byte alignment:
69890 switch (dataView.getUint16(tiffOffset)) {
69892 littleEndian = true;
69895 littleEndian = false;
69898 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
69901 // Check for the TIFF tag marker (0x002A):
69902 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
69903 Roo.log('Invalid Exif data: Missing TIFF marker.');
69906 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
69907 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
69909 this.parseExifTags(
69912 tiffOffset + dirOffset,
69917 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
69922 if (dirOffset + 6 > dataView.byteLength) {
69923 Roo.log('Invalid Exif data: Invalid directory offset.');
69926 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
69927 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
69928 if (dirEndOffset + 4 > dataView.byteLength) {
69929 Roo.log('Invalid Exif data: Invalid directory size.');
69932 for (i = 0; i < tagsNumber; i += 1) {
69936 dirOffset + 2 + 12 * i, // tag offset
69940 // Return the offset to the next directory:
69941 return dataView.getUint32(dirEndOffset, littleEndian);
69944 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
69946 var tag = dataView.getUint16(offset, littleEndian);
69948 this.exif[tag] = this.getExifValue(
69952 dataView.getUint16(offset + 2, littleEndian), // tag type
69953 dataView.getUint32(offset + 4, littleEndian), // tag length
69958 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
69960 var tagType = Roo.dialog.UploadCropbox.exifTagTypes[type],
69969 Roo.log('Invalid Exif data: Invalid tag type.');
69973 tagSize = tagType.size * length;
69974 // Determine if the value is contained in the dataOffset bytes,
69975 // or if the value at the dataOffset is a pointer to the actual data:
69976 dataOffset = tagSize > 4 ?
69977 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
69978 if (dataOffset + tagSize > dataView.byteLength) {
69979 Roo.log('Invalid Exif data: Invalid data offset.');
69982 if (length === 1) {
69983 return tagType.getValue(dataView, dataOffset, littleEndian);
69986 for (i = 0; i < length; i += 1) {
69987 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
69990 if (tagType.ascii) {
69992 // Concatenate the chars:
69993 for (i = 0; i < values.length; i += 1) {
69995 // Ignore the terminating NULL byte(s):
69996 if (c === '\u0000') {
70008 Roo.apply(Roo.dialog.UploadCropbox, {
70010 'Orientation': 0x0112
70014 1: 0, //'top-left',
70016 3: 180, //'bottom-right',
70017 // 4: 'bottom-left',
70019 6: 90, //'right-top',
70020 // 7: 'right-bottom',
70021 8: 270 //'left-bottom'
70025 // byte, 8-bit unsigned int:
70027 getValue: function (dataView, dataOffset) {
70028 return dataView.getUint8(dataOffset);
70032 // ascii, 8-bit byte:
70034 getValue: function (dataView, dataOffset) {
70035 return String.fromCharCode(dataView.getUint8(dataOffset));
70040 // short, 16 bit int:
70042 getValue: function (dataView, dataOffset, littleEndian) {
70043 return dataView.getUint16(dataOffset, littleEndian);
70047 // long, 32 bit int:
70049 getValue: function (dataView, dataOffset, littleEndian) {
70050 return dataView.getUint32(dataOffset, littleEndian);
70054 // rational = two long values, first is numerator, second is denominator:
70056 getValue: function (dataView, dataOffset, littleEndian) {
70057 return dataView.getUint32(dataOffset, littleEndian) /
70058 dataView.getUint32(dataOffset + 4, littleEndian);
70062 // slong, 32 bit signed int:
70064 getValue: function (dataView, dataOffset, littleEndian) {
70065 return dataView.getInt32(dataOffset, littleEndian);
70069 // srational, two slongs, first is numerator, second is denominator:
70071 getValue: function (dataView, dataOffset, littleEndian) {
70072 return dataView.getInt32(dataOffset, littleEndian) /
70073 dataView.getInt32(dataOffset + 4, littleEndian);
70083 cls : 'btn-group roo-upload-cropbox-rotate-left',
70084 action : 'rotate-left',
70088 cls : 'btn btn-default',
70089 html : '<i class="fa fa-undo"></i>'
70095 cls : 'btn-group roo-upload-cropbox-picture',
70096 action : 'picture',
70100 cls : 'btn btn-default',
70101 html : '<i class="fa fa-picture-o"></i>'
70107 cls : 'btn-group roo-upload-cropbox-rotate-right',
70108 action : 'rotate-right',
70112 cls : 'btn btn-default',
70113 html : '<i class="fa fa-repeat"></i>'
70121 cls : 'btn-group roo-upload-cropbox-rotate-left',
70122 action : 'rotate-left',
70126 cls : 'btn btn-default',
70127 html : '<i class="fa fa-undo"></i>'
70133 cls : 'btn-group roo-upload-cropbox-download',
70134 action : 'download',
70138 cls : 'btn btn-default',
70139 html : '<i class="fa fa-download"></i>'
70145 cls : 'btn-group roo-upload-cropbox-crop',
70150 cls : 'btn btn-default',
70151 html : '<i class="fa fa-crop"></i>'
70157 cls : 'btn-group roo-upload-cropbox-trash',
70162 cls : 'btn btn-default',
70163 html : '<i class="fa fa-trash"></i>'
70169 cls : 'btn-group roo-upload-cropbox-rotate-right',
70170 action : 'rotate-right',
70174 cls : 'btn btn-default',
70175 html : '<i class="fa fa-repeat"></i>'
70183 cls : 'btn-group roo-upload-cropbox-rotate-left',
70184 action : 'rotate-left',
70188 cls : 'btn btn-default',
70189 html : '<i class="fa fa-undo"></i>'
70195 cls : 'btn-group roo-upload-cropbox-rotate-right',
70196 action : 'rotate-right',
70200 cls : 'btn btn-default',
70201 html : '<i class="fa fa-repeat"></i>'
70209 cls : 'btn-group roo-upload-cropbox-center',
70214 cls : 'btn btn-default',