Roo.js
[roojs1] / Roo.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
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
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
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         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {
69                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
70                     return false; // no touch on chrome!?
71                 }
72                 document.createEvent("TouchEvent");  
73                 return true;  
74             } catch (e) {  
75                 return false;  
76             } 
77             
78         })();
79     // remove css image flicker
80         if(isIE && !isIE7){
81         try{
82             document.execCommand("BackgroundImageCache", false, true);
83         }catch(e){}
84     }
85     
86     Roo.apply(Roo, {
87         /**
88          * True if the browser is in strict mode
89          * @type Boolean
90          */
91         isStrict : isStrict,
92         /**
93          * True if the page is running over SSL
94          * @type Boolean
95          */
96         isSecure : isSecure,
97         /**
98          * True when the document is fully initialized and ready for action
99          * @type Boolean
100          */
101         isReady : false,
102         /**
103          * Turn on debugging output (currently only the factory uses this)
104          * @type Boolean
105          */
106         
107         debug: false,
108
109         /**
110          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
111          * @type Boolean
112          */
113         enableGarbageCollector : true,
114
115         /**
116          * True to automatically purge event listeners after uncaching an element (defaults to false).
117          * Note: this only happens if enableGarbageCollector is true.
118          * @type Boolean
119          */
120         enableListenerCollection:false,
121
122         /**
123          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
124          * the IE insecure content warning (defaults to javascript:false).
125          * @type String
126          */
127         SSL_SECURE_URL : "javascript:false",
128
129         /**
130          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
131          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
132          * @type String
133          */
134         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
135
136         emptyFn : function(){},
137         
138         /**
139          * Copies all the properties of config to obj if they don't already exist.
140          * @param {Object} obj The receiver of the properties
141          * @param {Object} config The source of the properties
142          * @return {Object} returns obj
143          */
144         applyIf : function(o, c){
145             if(o && c){
146                 for(var p in c){
147                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
148                 }
149             }
150             return o;
151         },
152
153         /**
154          * Applies event listeners to elements by selectors when the document is ready.
155          * The event name is specified with an @ suffix.
156 <pre><code>
157 Roo.addBehaviors({
158    // add a listener for click on all anchors in element with id foo
159    '#foo a@click' : function(e, t){
160        // do something
161    },
162
163    // add the same listener to multiple selectors (separated by comma BEFORE the @)
164    '#foo a, #bar span.some-class@mouseover' : function(){
165        // do something
166    }
167 });
168 </code></pre>
169          * @param {Object} obj The list of behaviors to apply
170          */
171         addBehaviors : function(o){
172             if(!Roo.isReady){
173                 Roo.onReady(function(){
174                     Roo.addBehaviors(o);
175                 });
176                 return;
177             }
178             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
179             for(var b in o){
180                 var parts = b.split('@');
181                 if(parts[1]){ // for Object prototype breakers
182                     var s = parts[0];
183                     if(!cache[s]){
184                         cache[s] = Roo.select(s);
185                     }
186                     cache[s].on(parts[1], o[b]);
187                 }
188             }
189             cache = null;
190         },
191
192         /**
193          * Generates unique ids. If the element already has an id, it is unchanged
194          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
195          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
196          * @return {String} The generated Id.
197          */
198         id : function(el, prefix){
199             prefix = prefix || "roo-gen";
200             el = Roo.getDom(el);
201             var id = prefix + (++idSeed);
202             return el ? (el.id ? el.id : (el.id = id)) : id;
203         },
204          
205        
206         /**
207          * Extends one class with another class and optionally overrides members with the passed literal. This class
208          * also adds the function "override()" to the class that can be used to override
209          * members on an instance.
210          * @param {Object} subclass The class inheriting the functionality
211          * @param {Object} superclass The class being extended
212          * @param {Object} overrides (optional) A literal with members
213          * @method extend
214          */
215         extend : function(){
216             // inline overrides
217             var io = function(o){
218                 for(var m in o){
219                     this[m] = o[m];
220                 }
221             };
222             return function(sb, sp, overrides){
223                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
224                     overrides = sp;
225                     sp = sb;
226                     sb = function(){sp.apply(this, arguments);};
227                 }
228                 var F = function(){}, sbp, spp = sp.prototype;
229                 F.prototype = spp;
230                 sbp = sb.prototype = new F();
231                 sbp.constructor=sb;
232                 sb.superclass=spp;
233                 
234                 if(spp.constructor == Object.prototype.constructor){
235                     spp.constructor=sp;
236                    
237                 }
238                 
239                 sb.override = function(o){
240                     Roo.override(sb, o);
241                 };
242                 sbp.override = io;
243                 Roo.override(sb, overrides);
244                 return sb;
245             };
246         }(),
247
248         /**
249          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
250          * Usage:<pre><code>
251 Roo.override(MyClass, {
252     newMethod1: function(){
253         // etc.
254     },
255     newMethod2: function(foo){
256         // etc.
257     }
258 });
259  </code></pre>
260          * @param {Object} origclass The class to override
261          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
262          * containing one or more methods.
263          * @method override
264          */
265         override : function(origclass, overrides){
266             if(overrides){
267                 var p = origclass.prototype;
268                 for(var method in overrides){
269                     p[method] = overrides[method];
270                 }
271             }
272         },
273         /**
274          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
275          * <pre><code>
276 Roo.namespace('Company', 'Company.data');
277 Company.Widget = function() { ... }
278 Company.data.CustomStore = function(config) { ... }
279 </code></pre>
280          * @param {String} namespace1
281          * @param {String} namespace2
282          * @param {String} etc
283          * @method namespace
284          */
285         namespace : function(){
286             var a=arguments, o=null, i, j, d, rt;
287             for (i=0; i<a.length; ++i) {
288                 d=a[i].split(".");
289                 rt = d[0];
290                 /** eval:var:o */
291                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
292                 for (j=1; j<d.length; ++j) {
293                     o[d[j]]=o[d[j]] || {};
294                     o=o[d[j]];
295                 }
296             }
297         },
298         /**
299          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
300          * <pre><code>
301 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
302 Roo.factory(conf, Roo.data);
303 </code></pre>
304          * @param {String} classname
305          * @param {String} namespace (optional)
306          * @method factory
307          */
308          
309         factory : function(c, ns)
310         {
311             // no xtype, no ns or c.xns - or forced off by c.xns
312             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
313                 return c;
314             }
315             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
316             if (c.constructor == ns[c.xtype]) {// already created...
317                 return c;
318             }
319             if (ns[c.xtype]) {
320                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
321                 var ret = new ns[c.xtype](c);
322                 ret.xns = false;
323                 return ret;
324             }
325             c.xns = false; // prevent recursion..
326             return c;
327         },
328          /**
329          * Logs to console if it can.
330          *
331          * @param {String|Object} string
332          * @method log
333          */
334         log : function(s)
335         {
336             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
337                 return; // alerT?
338             }
339             console.log(s);
340             
341         },
342         /**
343          * 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.
344          * @param {Object} o
345          * @return {String}
346          */
347         urlEncode : function(o){
348             if(!o){
349                 return "";
350             }
351             var buf = [];
352             for(var key in o){
353                 var ov = o[key], k = Roo.encodeURIComponent(key);
354                 var type = typeof ov;
355                 if(type == 'undefined'){
356                     buf.push(k, "=&");
357                 }else if(type != "function" && type != "object"){
358                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
359                 }else if(ov instanceof Array){
360                     if (ov.length) {
361                             for(var i = 0, len = ov.length; i < len; i++) {
362                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
363                             }
364                         } else {
365                             buf.push(k, "=&");
366                         }
367                 }
368             }
369             buf.pop();
370             return buf.join("");
371         },
372          /**
373          * Safe version of encodeURIComponent
374          * @param {String} data 
375          * @return {String} 
376          */
377         
378         encodeURIComponent : function (data)
379         {
380             try {
381                 return encodeURIComponent(data);
382             } catch(e) {} // should be an uri encode error.
383             
384             if (data == '' || data == null){
385                return '';
386             }
387             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
388             function nibble_to_hex(nibble){
389                 var chars = '0123456789ABCDEF';
390                 return chars.charAt(nibble);
391             }
392             data = data.toString();
393             var buffer = '';
394             for(var i=0; i<data.length; i++){
395                 var c = data.charCodeAt(i);
396                 var bs = new Array();
397                 if (c > 0x10000){
398                         // 4 bytes
399                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
400                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
401                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
402                     bs[3] = 0x80 | (c & 0x3F);
403                 }else if (c > 0x800){
404                          // 3 bytes
405                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
406                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
407                     bs[2] = 0x80 | (c & 0x3F);
408                 }else if (c > 0x80){
409                        // 2 bytes
410                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
411                     bs[1] = 0x80 | (c & 0x3F);
412                 }else{
413                         // 1 byte
414                     bs[0] = c;
415                 }
416                 for(var j=0; j<bs.length; j++){
417                     var b = bs[j];
418                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
419                             + nibble_to_hex(b &0x0F);
420                     buffer += '%'+hex;
421                }
422             }
423             return buffer;    
424              
425         },
426
427         /**
428          * 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]}.
429          * @param {String} string
430          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
431          * @return {Object} A literal with members
432          */
433         urlDecode : function(string, overwrite){
434             if(!string || !string.length){
435                 return {};
436             }
437             var obj = {};
438             var pairs = string.split('&');
439             var pair, name, value;
440             for(var i = 0, len = pairs.length; i < len; i++){
441                 pair = pairs[i].split('=');
442                 name = decodeURIComponent(pair[0]);
443                 value = decodeURIComponent(pair[1]);
444                 if(overwrite !== true){
445                     if(typeof obj[name] == "undefined"){
446                         obj[name] = value;
447                     }else if(typeof obj[name] == "string"){
448                         obj[name] = [obj[name]];
449                         obj[name].push(value);
450                     }else{
451                         obj[name].push(value);
452                     }
453                 }else{
454                     obj[name] = value;
455                 }
456             }
457             return obj;
458         },
459
460         /**
461          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
462          * passed array is not really an array, your function is called once with it.
463          * The supplied function is called with (Object item, Number index, Array allItems).
464          * @param {Array/NodeList/Mixed} array
465          * @param {Function} fn
466          * @param {Object} scope
467          */
468         each : function(array, fn, scope){
469             if(typeof array.length == "undefined" || typeof array == "string"){
470                 array = [array];
471             }
472             for(var i = 0, len = array.length; i < len; i++){
473                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
474             }
475         },
476
477         // deprecated
478         combine : function(){
479             var as = arguments, l = as.length, r = [];
480             for(var i = 0; i < l; i++){
481                 var a = as[i];
482                 if(a instanceof Array){
483                     r = r.concat(a);
484                 }else if(a.length !== undefined && !a.substr){
485                     r = r.concat(Array.prototype.slice.call(a, 0));
486                 }else{
487                     r.push(a);
488                 }
489             }
490             return r;
491         },
492
493         /**
494          * Escapes the passed string for use in a regular expression
495          * @param {String} str
496          * @return {String}
497          */
498         escapeRe : function(s) {
499             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
500         },
501
502         // internal
503         callback : function(cb, scope, args, delay){
504             if(typeof cb == "function"){
505                 if(delay){
506                     cb.defer(delay, scope, args || []);
507                 }else{
508                     cb.apply(scope, args || []);
509                 }
510             }
511         },
512
513         /**
514          * Return the dom node for the passed string (id), dom node, or Roo.Element
515          * @param {String/HTMLElement/Roo.Element} el
516          * @return HTMLElement
517          */
518         getDom : function(el){
519             if(!el){
520                 return null;
521             }
522             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
523         },
524
525         /**
526         * Shorthand for {@link Roo.ComponentMgr#get}
527         * @param {String} id
528         * @return Roo.Component
529         */
530         getCmp : function(id){
531             return Roo.ComponentMgr.get(id);
532         },
533          
534         num : function(v, defaultValue){
535             if(typeof v != 'number'){
536                 return defaultValue;
537             }
538             return v;
539         },
540
541         destroy : function(){
542             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
543                 var as = a[i];
544                 if(as){
545                     if(as.dom){
546                         as.removeAllListeners();
547                         as.remove();
548                         continue;
549                     }
550                     if(typeof as.purgeListeners == 'function'){
551                         as.purgeListeners();
552                     }
553                     if(typeof as.destroy == 'function'){
554                         as.destroy();
555                     }
556                 }
557             }
558         },
559
560         // inpired by a similar function in mootools library
561         /**
562          * Returns the type of object that is passed in. If the object passed in is null or undefined it
563          * return false otherwise it returns one of the following values:<ul>
564          * <li><b>string</b>: If the object passed is a string</li>
565          * <li><b>number</b>: If the object passed is a number</li>
566          * <li><b>boolean</b>: If the object passed is a boolean value</li>
567          * <li><b>function</b>: If the object passed is a function reference</li>
568          * <li><b>object</b>: If the object passed is an object</li>
569          * <li><b>array</b>: If the object passed is an array</li>
570          * <li><b>regexp</b>: If the object passed is a regular expression</li>
571          * <li><b>element</b>: If the object passed is a DOM Element</li>
572          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
573          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
574          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
575          * @param {Mixed} object
576          * @return {String}
577          */
578         type : function(o){
579             if(o === undefined || o === null){
580                 return false;
581             }
582             if(o.htmlElement){
583                 return 'element';
584             }
585             var t = typeof o;
586             if(t == 'object' && o.nodeName) {
587                 switch(o.nodeType) {
588                     case 1: return 'element';
589                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
590                 }
591             }
592             if(t == 'object' || t == 'function') {
593                 switch(o.constructor) {
594                     case Array: return 'array';
595                     case RegExp: return 'regexp';
596                 }
597                 if(typeof o.length == 'number' && typeof o.item == 'function') {
598                     return 'nodelist';
599                 }
600             }
601             return t;
602         },
603
604         /**
605          * Returns true if the passed value is null, undefined or an empty string (optional).
606          * @param {Mixed} value The value to test
607          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
608          * @return {Boolean}
609          */
610         isEmpty : function(v, allowBlank){
611             return v === null || v === undefined || (!allowBlank ? v === '' : false);
612         },
613         
614         /** @type Boolean */
615         isOpera : isOpera,
616         /** @type Boolean */
617         isSafari : isSafari,
618         /** @type Boolean */
619         isFirefox : isFirefox,
620         /** @type Boolean */
621         isIE : isIE,
622         /** @type Boolean */
623         isIE7 : isIE7,
624         /** @type Boolean */
625         isIE11 : isIE11,
626         /** @type Boolean */
627         isGecko : isGecko,
628         /** @type Boolean */
629         isBorderBox : isBorderBox,
630         /** @type Boolean */
631         isWindows : isWindows,
632         /** @type Boolean */
633         isLinux : isLinux,
634         /** @type Boolean */
635         isMac : isMac,
636         /** @type Boolean */
637         isIOS : isIOS,
638         /** @type Boolean */
639         isTouch : isTouch,
640
641         /**
642          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
643          * you may want to set this to true.
644          * @type Boolean
645          */
646         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
647         
648         
649                 
650         /**
651          * Selects a single element as a Roo Element
652          * This is about as close as you can get to jQuery's $('do crazy stuff')
653          * @param {String} selector The selector/xpath query
654          * @param {Node} root (optional) The start of the query (defaults to document).
655          * @return {Roo.Element}
656          */
657         selectNode : function(selector, root) 
658         {
659             var node = Roo.DomQuery.selectNode(selector,root);
660             return node ? Roo.get(node) : new Roo.Element(false);
661         }
662         
663     });
664
665
666 })();
667
668 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
669                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
670                 "Roo.app", "Roo.ux",
671                 "Roo.bootstrap",
672                 "Roo.bootstrap.dash");