diff --git a/ajax/libs/qooxdoo/4.0.1/indigo.css b/ajax/libs/qooxdoo/4.0.1/indigo.css new file mode 100644 index 000000000..55945ca36 --- /dev/null +++ b/ajax/libs/qooxdoo/4.0.1/indigo.css @@ -0,0 +1 @@ +html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;vertical-align:baseline}html{font-size:100%}.qx-widget{overflow-x:hidden;overflow-y:hidden;outline:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.qx-widget[disabled],.qx-widget[disabled] *{cursor:default;pointer-events:none}.qx-widget *::-moz-focus-inner{border:0}.qx-button[disabled]{border:0.0625rem solid #bbb;color:#a0a0a0}.qx-button[disabled] img{opacity:0.4}.qx-button{display:inline-block;padding:0.5rem 0.75rem;margin:0.1875rem;border:0.0625rem solid #bbb;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;-moz-border-radius:0.125rem;-webkit-border-radius:0.125rem;border-radius:0.125rem;cursor:pointer;font-size:0.875rem;outline:none;background:#fff;color:#619edf;line-height:normal;min-height:2.1875rem}.qx-button:hover{border:0.0625rem solid #619edf}.qx-button:active{border:0.0625rem solid #256bb5;background:#e6e6e6}.qx-button:focus{border-color:#619edf}.qx-button img{margin-left:0.1875rem}.qx-button img,span{vertical-align:middle}.qx-calendar table{border-width:0.0625rem;border-color:#bbb;border-collapse:collapse;text-align:center;border-spacing:0}.qx-calendar button{cursor:pointer}.qx-calendar button:focus{-webkit-text-stroke:0.0625rem #619edf;color:#619edf}.qx-calendar thead{border-width:0.0625rem;border-style:solid;border-color:#bbb;font-weight:bold}.qx-calendar-othermonth button{color:#a0a0a0}.qx-calendar-today{background-color:#ededed}.qx-calendar thead td{padding:0.125rem 0.375rem;width:14.3%}.qx-calendar td{border-width:0.0625rem;border-style:solid;border-color:#bbb;background-color:#fff}.qx-calendar td button{margin:0;width:100%;line-height:2;border:none;background:none;outline:none}.qx-calendar td button:disabled{pointer-events:none}.qx-calendar thead tr:first-child td:first-child{border-right:0}.qx-calendar thead tr:first-child td:nth-child(2){border-left:0;border-right:0;text-align:center}.qx-calendar thead tr:first-child td:last-child{border-left:0;text-align:right}td.qx-calendar-selected{background-color:#619edf}td.qx-calendar-selected button{color:#fff}td.qx-calendar-selected button:focus{-webkit-text-stroke:0;color:#fff}.qx-calendar table[disabled]{color:#a0a0a0}.qx-calendar[disabled] .selected{background-color:#a0a0a0}.qx-menu{overflow-x:hidden;overflow-y:hidden;z-index:1000001;position:absolute;background-color:#fff;border:0.0625rem solid #bbb;padding:0;margin:0}.qx-menu li{padding:0.375rem 0.625rem;list-style-type:none;cursor:pointer}.qx-menu li:hover{color:#fff;background-color:#619edf}.qx-rating-item{font-size:1.375rem;background-image:-webkit-linear-gradient(270deg,#e3b64a,#d5771d);color:#E3B64A;-webkit-background-clip:text;-moz-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;-webkit-text-stroke:0.0625rem #bb6718;margin-right:0.625rem;cursor:pointer}.qx-rating-item.qx-rating-item-off{background-image:-webkit-linear-gradient(270deg,#eee,#cfcfcf);color:#cfcfcf;-webkit-background-clip:text;-moz-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;-webkit-text-stroke:0.0625rem #cfcfcf}.qx-rating-item[disabled]{background-image:-webkit-linear-gradient(270deg,#979797,#797979);-webkit-text-stroke:0.0625rem #6a6a6a;color:#797979}.qx-rating-item.qx-rating-item-off[disabled]{background-image:-webkit-linear-gradient(270deg,#eee,#cfcfcf);-webkit-text-stroke:0.0625rem #cfcfcf;color:#cfcfcf}.qx-slider{overflow-y:visible;overflow-x:visible;cursor:pointer;padding:0 1.125rem}.qx-slider:before{position:relative;content:'';width:100%;line-height:0.3;border:0.0625rem solid #7b7b7b;display:inline-block;-moz-border-radius:0.1875rem;-webkit-border-radius:0.1875rem;border-radius:0.1875rem;background-color:#8e8e8e;background-image:-webkit-linear-gradient(#8e8e8e,#b7b7b7);background-image:-moz-linear-gradient(#8e8e8e,#b7b7b7);background-image:-ms-linear-gradient(#8e8e8e,#b7b7b7);background-image:-webkit-gradient(linear, center top, center bottom, color-stop(0, #8e8e8e), color-stop(1, #b7b7b7));background-image:linear-gradient(#8e8e8e,#b7b7b7);top:1.625rem;height:0.4375rem;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.qx-slider-knob{cursor:pointer;display:inline-block;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:2.25rem;height:2.25rem;font-weight:bold;text-align:center;border:0.0625rem solid #7b7b7b;background-color:#f7f7f7;background-image:-webkit-linear-gradient(#f7f7f7,#e8e8e8);background-image:-moz-linear-gradient(#f7f7f7,#e8e8e8);background-image:-ms-linear-gradient(#f7f7f7,#e8e8e8);background-image:-webkit-gradient(linear, center top, center bottom, color-stop(0, #f7f7f7), color-stop(1, #e8e8e8));background-image:linear-gradient(#f7f7f7,#e8e8e8);-moz-border-radius:3.75rem;-webkit-border-radius:3.75rem;border-radius:3.75rem;line-height:0}.qx-slider-knob:active{background-color:#b7b7b7;background-image:-webkit-linear-gradient(#b7b7b7,#a8a8a8);background-image:-moz-linear-gradient(#b7b7b7,#a8a8a8);background-image:-ms-linear-gradient(#b7b7b7,#a8a8a8);background-image:-webkit-gradient(linear, center top, center bottom, color-stop(0, #b7b7b7), color-stop(1, #a8a8a8));background-image:linear-gradient(#b7b7b7,#a8a8a8);color:#fff}.qx-slider[disabled]:before{border:0.0625rem solid #e1e1e1;background-color:#f4f4f4;background-image:-webkit-linear-gradient(#f4f4f4,#eaeaea);background-image:-moz-linear-gradient(#f4f4f4,#eaeaea);background-image:-ms-linear-gradient(#f4f4f4,#eaeaea);background-image:-webkit-gradient(linear, center top, center bottom, color-stop(0, #f4f4f4), color-stop(1, #eaeaea));background-image:linear-gradient(#f4f4f4,#eaeaea)}.qx-slider-knob[disabled]{border:0.0625rem solid #c8c8c8;background-color:#f7f7f7;background-image:-webkit-linear-gradient(#f7f7f7,#f5f5f5);background-image:-moz-linear-gradient(#f7f7f7,#f5f5f5);background-image:-ms-linear-gradient(#f7f7f7,#f5f5f5);background-image:-webkit-gradient(linear, center top, center bottom, color-stop(0, #f7f7f7), color-stop(1, #f5f5f5));background-image:linear-gradient(#f7f7f7,#f5f5f5);color:#bbb}.qx-slider-knob:focus{outline:none}.qx-toolbar{overflow:hidden;border-bottom:0.0625rem solid #bbb;border-top:0.0625rem solid #bbb;padding:0.4375rem;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:100%;background-color:#F7F7F7}@media all and (max-width: 53.125rem){.qx-toolbar{padding:0}.qx-toolbar .qx-button{display:block;width:100%;background:none;border:0;border-top:0.0625rem solid #bbb}.qx-toolbar .qx-button-disabled{display:block;width:100%;border:0 !important;background-image:none !important;border-top:0.0625rem solid #bbb !important}.qx-toolbar .qx-button:hover{border:0 solid #939393;text-decoration:underline;border-top:0.0625rem solid #bbb}.qx-toolbar .qx-button:active{border:0;background-color:transparent;background-image:none;border-top:0.0625rem solid #bbb}.qx-toolbar .qx-button:focus{border:0}}.qx-tabs>ul{list-style:none;margin-bottom:0;padding:0}.qx-tabs>ul:after{content:" ";display:block;clear:both}.qx-tabs-button:first-child{margin-left:0}.qx-tabs-button{float:left;margin-bottom:-0.09375rem;cursor:pointer;border:0.0625rem solid transparent;margin-left:0.1875rem}.qx-tabs-button>button{background-color:transparent;border:0;color:#619edf;outline:none;cursor:pointer;padding:0.5rem 0.75rem;font-size:0.8125rem}.qx-tabs-button>button[disabled]{color:#a0a0a0}.qx-tabs-button>button:focus{text-decoration:underline}.qx-tabs-button-active{border:0.0625rem solid #bbb;border-bottom:0.0625rem solid #fff;-moz-border-radius:0.125rem 0.125rem 0 0;-webkit-border-radius:0.125rem 0.125rem 0 0;border-radius:0.125rem 0.125rem 0 0;background-color:#fff}.qx-tabs-button:hover{border-bottom:0.0625rem solid #bbb;-moz-border-radius:0.125rem 0.125rem 0 0;-webkit-border-radius:0.125rem 0.125rem 0 0;border-radius:0.125rem 0.125rem 0 0;background-color:#e6e6e6}.qx-tabs-button-active:hover{border:0.0625rem solid #bbb;border-bottom:0.0625rem solid #fff;background-color:#fff}.qx-tabs-button-active>button{color:black}.qx-tabs-container{border:0.0625rem solid #bbb}.qx-tabs-page{padding:0.625rem;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.qx-tabs-button[disabled]{color:#a0a0a0}.qx-tabs-justify>li{display:table-cell;width:1%;text-align:center;float:none;border-bottom-color:transparent;position:relative;-webkit-transform:translate(0, 0.075rem);-moz-transform:translate(0, 0.075rem);-ms-transform:translate(0, 0.075rem);transform:translate(0, 0.075rem)}.qx-tabs-justify>.qx-tabs-button-active{border-bottom:0.0625rem solid #fff}.qx-tabs-right>li{float:right}.qx-tabs-right>.qx-tabs-button:first-child{margin-left:0.1875rem}.qx-tabs-right>.qx-tabs-button:last-child{margin-left:0}.qx-accordion{border-right:0.0625rem solid #bbb;border-bottom:0.0625rem solid #bbb;border-left:0.0625rem solid #bbb}.qx-accordion>ul{list-style:none;margin-bottom:0;padding:0}.qx-accordion-button{margin-left:0;border-top:0.0625rem solid #bbb;cursor:pointer}.qx-accordion-button>button{display:inline-block;width:100%;height:100%;padding:0.5rem 0.75rem;border:none;outline:none;font-size:0.8125rem;background-color:transparent;color:#619edf;text-align:left}.qx-accordion-button>button[disabled]{color:#a0a0a0}.qx-accordion-button>button:focus{text-decoration:underline}.qx-accordion-button-active{background-color:#fff}.qx-accordion-button:hover{background-color:#e6e6e6}.qx-accordion-button-active:hover{background-color:#fff}.qx-accordion-button-active>button{color:black}.qx-accordion-page{overflow:hidden;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0.5rem 0.75rem;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;background-color:white;-webkit-transition:height 500ms,padding-top 500ms,padding-bottom 500ms,margin-top 500ms,margin-bottom 500ms;-moz-transition:height 500ms,padding-top 500ms,padding-bottom 500ms,margin-top 500ms,margin-bottom 500ms;transition:height 500ms,padding-top 500ms,padding-bottom 500ms,margin-top 500ms,margin-bottom 500ms}.qx-accordion-page-closed{height:0px;padding-top:0px;padding-bottom:0px}.qx-accordion-button[disabled]{color:#a0a0a0}.qx-datepicker{font-size:0.875rem}.qx-datepicker-icon{cursor:pointer;vertical-align:top;margin-left:0.25rem;width:1.375rem;height:1.375rem}.qx-datepicker[disabled]+.qx-datepicker-icon{cursor:default} diff --git a/ajax/libs/qooxdoo/4.0.1/q.js b/ajax/libs/qooxdoo/4.0.1/q.js new file mode 100644 index 000000000..e6f42d9f3 --- /dev/null +++ b/ajax/libs/qooxdoo/4.0.1/q.js @@ -0,0 +1,59133 @@ +/** qooxdoo v4.0.1 | (c) 2013 1&1 Internet AG, http://1und1.de | http://qooxdoo.org/license */ +(function(){ +if (!window.qx) window.qx = {}; +var qx = window.qx; + +if (!qx.$$environment) qx.$$environment = {}; +var envinfo = {"json":true,"qx.application":"library.Application","qx.debug":false,"qx.debug.databinding":false,"qx.debug.dispose":false,"qx.debug.io":false,"qx.debug.ui.queue":false,"qx.globalErrorHandling":false,"qx.optimization.variants":true,"qx.revision":"","qx.theme":"qx.theme.Modern","qx.version":"4.0.1"}; +for (var k in envinfo) qx.$$environment[k] = envinfo[k]; + +qx.$$packageData = {}; + +/** qooxdoo v4.0.1 | (c) 2013 1&1 Internet AG, http://1und1.de | http://qooxdoo.org/license */ +qx.$$packageData['0']={"locales":{},"resources":{},"translations":{"C":{},"en":{}}}; + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Sebastian Werner (wpbasti) + * Andreas Ecker (ecker) + * Martin Wittemann (martinwittemann) + +************************************************************************ */ +/** + * Create namespace + * + * @ignore(qx.data) + * @ignore(qx.data.IListData) + * @ignore(qx.util.OOUtil) + */ +if(!window.qx){ + + window.qx = { + }; +}; +/** + * Bootstrap qx.Bootstrap to create myself later + * This is needed for the API browser etc. to let them detect me + */ +qx.Bootstrap = { + genericToString : function(){ + + return "[Class " + this.classname + "]"; + }, + createNamespace : function(name, object){ + + var splits = name.split("."); + var part = splits[0]; + var parent = qx.$$namespaceRoot && qx.$$namespaceRoot[part] ? qx.$$namespaceRoot : window; + for(var i = 0,len = splits.length - 1;i < len;i++,part = splits[i]){ + + if(!parent[part]){ + + parent = parent[part] = { + }; + } else { + + parent = parent[part]; + }; + }; + // store object + parent[part] = object; + // return last part name (e.g. classname) + return part; + }, + setDisplayName : function(fcn, classname, name){ + + fcn.displayName = classname + "." + name + "()"; + }, + setDisplayNames : function(functionMap, classname){ + + for(var name in functionMap){ + + var value = functionMap[name]; + if(value instanceof Function){ + + value.displayName = classname + "." + name + "()"; + }; + }; + }, + base : function(args, varargs){ + + if(qx.Bootstrap.DEBUG){ + + if(!qx.Bootstrap.isFunction(args.callee.base)){ + + throw new Error("Cannot call super class. Method is not derived: " + args.callee.displayName); + }; + }; + if(arguments.length === 1){ + + return args.callee.base.call(this); + } else { + + return args.callee.base.apply(this, Array.prototype.slice.call(arguments, 1)); + }; + }, + define : function(name, config){ + + if(!config){ + + config = { + statics : { + } + }; + }; + var clazz; + var proto = null; + qx.Bootstrap.setDisplayNames(config.statics, name); + if(config.members || config.extend){ + + qx.Bootstrap.setDisplayNames(config.members, name + ".prototype"); + clazz = config.construct || new Function; + if(config.extend){ + + this.extendClass(clazz, clazz, config.extend, name, basename); + }; + var statics = config.statics || { + }; + // use keys to include the shadowed in IE + for(var i = 0,keys = qx.Bootstrap.keys(statics),l = keys.length;i < l;i++){ + + var key = keys[i]; + clazz[key] = statics[key]; + }; + proto = clazz.prototype; + // Enable basecalls within constructor + proto.base = qx.Bootstrap.base; + proto.name = proto.classname = name; + var members = config.members || { + }; + var key,member; + // use keys to include the shadowed in IE + for(var i = 0,keys = qx.Bootstrap.keys(members),l = keys.length;i < l;i++){ + + key = keys[i]; + member = members[key]; + // Enable basecalls for methods + // Hint: proto[key] is not yet overwritten here + if(member instanceof Function && proto[key]){ + + member.base = proto[key]; + }; + proto[key] = member; + }; + } else { + + clazz = config.statics || { + }; + // Merge class into former class (needed for 'optimize: ["statics"]') + if(qx.Bootstrap.$$registry && qx.Bootstrap.$$registry[name]){ + + var formerClass = qx.Bootstrap.$$registry[name]; + // Add/overwrite properties and return early if necessary + if(this.keys(clazz).length !== 0){ + + // Execute defer to prevent too early overrides + if(config.defer){ + + config.defer(clazz, proto); + }; + for(var curProp in clazz){ + + formerClass[curProp] = clazz[curProp]; + }; + return formerClass; + }; + }; + }; + // Store type info + clazz.$$type = "Class"; + // Attach toString + if(!clazz.hasOwnProperty("toString")){ + + clazz.toString = this.genericToString; + }; + // Create namespace + var basename = name ? this.createNamespace(name, clazz) : ""; + // Store names in constructor/object + clazz.name = clazz.classname = name; + clazz.basename = basename; + clazz.$$events = config.events; + // Execute defer section + if(config.defer){ + + config.defer(clazz, proto); + }; + // Store class reference in global class registry + if(name != null){ + + qx.Bootstrap.$$registry[name] = clazz; + }; + return clazz; + } +}; +/** + * Internal class that is responsible for bootstrapping the qooxdoo + * framework at load time. + */ +qx.Bootstrap.define("qx.Bootstrap", { + statics : { + /** Timestamp of qooxdoo based application startup */ + LOADSTART : qx.$$start || new Date(), + /** + * Mapping for early use of the qx.debug environment setting. + */ + DEBUG : (function(){ + + // make sure to reflect all changes here to the environment class! + var debug = true; + if(qx.$$environment && qx.$$environment["qx.debug"] === false){ + + debug = false; + }; + return debug; + })(), + /** + * Minimal accessor API for the environment settings given from the + * generator. + * + * WARNING: This method only should be used if the + * {@link qx.core.Environment} class is not loaded! + * + * @param key {String} The key to get the value from. + * @return {var} The value of the setting or undefined. + */ + getEnvironmentSetting : function(key){ + + if(qx.$$environment){ + + return qx.$$environment[key]; + }; + }, + /** + * Minimal mutator for the environment settings given from the generator. + * It checks for the existance of the environment settings and sets the + * key if its not given from the generator. If a setting is available from + * the generator, the setting will be ignored. + * + * WARNING: This method only should be used if the + * {@link qx.core.Environment} class is not loaded! + * + * @param key {String} The key of the setting. + * @param value {var} The value for the setting. + */ + setEnvironmentSetting : function(key, value){ + + if(!qx.$$environment){ + + qx.$$environment = { + }; + }; + if(qx.$$environment[key] === undefined){ + + qx.$$environment[key] = value; + }; + }, + /** + * Creates a namespace and assigns the given object to it. + * + * @internal + * @signature function(name, object) + * @param name {String} The complete namespace to create. Typically, the last part is the class name itself + * @param object {Object} The object to attach to the namespace + * @return {String} last part of the namespace (which object is assigned to) + * @throws {Error} when the given object already exists. + */ + createNamespace : qx.Bootstrap.createNamespace, + /** + * Offers the ability to change the root for creating namespaces from window to + * whatever object is given. + * + * @param root {Object} The root to use. + * @internal + */ + setRoot : function(root){ + + qx.$$namespaceRoot = root; + }, + /** + * Call the same method of the super class. + * + * @signature function(args, varargs) + * @param args {arguments} the arguments variable of the calling method + * @param varargs {var} variable number of arguments passed to the overwritten function + * @return {var} the return value of the method of the base class. + */ + base : qx.Bootstrap.base, + /** + * Define a new class using the qooxdoo class system. + * Lightweight version of {@link qx.Class#define} with less features. + * + * @signature function(name, config) + * @param name {String?} Name of the class. If null, the class will not be + * attached to a namespace. + * @param config {Map ? null} Class definition structure. The configuration map has the following keys: + * + * + * + * + * + * + * + *
NameTypeDescription
extendClassThe super class the current class inherits from.
constructFunctionThe constructor of the class.
staticsMapMap of static values / functions of the class.
membersMapMap of instance members of the class.
deferFunctionFunction that is called at the end of + * processing the class declaration.
+ * @return {Class} The defined class. + */ + define : qx.Bootstrap.define, + /** + * Sets the display name of the given function + * + * @signature function(fcn, classname, name) + * @param fcn {Function} the function to set the display name for + * @param classname {String} the name of the class the function is defined in + * @param name {String} the function name + */ + setDisplayName : qx.Bootstrap.setDisplayName, + /** + * Set the names of all functions defined in the given map + * + * @signature function(functionMap, classname) + * @param functionMap {Object} a map with functions as values + * @param classname {String} the name of the class, the functions are + * defined in + */ + setDisplayNames : qx.Bootstrap.setDisplayNames, + /** + * This method will be attached to all classes to return + * a nice identifier for them. + * + * @internal + * @signature function() + * @return {String} The class identifier + */ + genericToString : qx.Bootstrap.genericToString, + /** + * Inherit a clazz from a super class. + * + * This function differentiates between class and constructor because the + * constructor written by the user might be wrapped and the base + * property has to be attached to the constructor, while the superclass + * property has to be attached to the wrapped constructor. + * + * @param clazz {Function} The class's wrapped constructor + * @param construct {Function} The unwrapped constructor + * @param superClass {Function} The super class + * @param name {Function} fully qualified class name + * @param basename {Function} the base name + */ + extendClass : function(clazz, construct, superClass, name, basename){ + + var superproto = superClass.prototype; + // Use helper function/class to save the unnecessary constructor call while + // setting up inheritance. + var helper = new Function(); + helper.prototype = superproto; + var proto = new helper(); + // Apply prototype to new helper instance + clazz.prototype = proto; + // Store names in prototype + proto.name = proto.classname = name; + proto.basename = basename; + /* + - Store base constructor to constructor- + - Store reference to extend class + */ + construct.base = superClass; + clazz.superclass = superClass; + /* + - Store statics/constructor onto constructor/prototype + - Store correct constructor + - Store statics onto prototype + */ + construct.self = clazz.constructor = proto.constructor = clazz; + }, + /** + * Find a class by its name + * + * @param name {String} class name to resolve + * @return {Class} the class + */ + getByName : function(name){ + + return qx.Bootstrap.$$registry[name]; + }, + /** @type {Map} Stores all defined classes */ + $$registry : { + }, + /* + --------------------------------------------------------------------------- + OBJECT UTILITY FUNCTIONS + --------------------------------------------------------------------------- + */ + /** + * Get the number of own properties in the object. + * + * @param map {Object} the map + * @return {Integer} number of objects in the map + * @lint ignoreUnused(key) + */ + objectGetLength : function(map){ + + return qx.Bootstrap.keys(map).length; + }, + /** + * Inserts all keys of the source object into the + * target objects. Attention: The target map gets modified. + * + * @param target {Object} target object + * @param source {Object} object to be merged + * @param overwrite {Boolean ? true} If enabled existing keys will be overwritten + * @return {Object} Target with merged values from the source object + */ + objectMergeWith : function(target, source, overwrite){ + + if(overwrite === undefined){ + + overwrite = true; + }; + for(var key in source){ + + if(overwrite || target[key] === undefined){ + + target[key] = source[key]; + }; + }; + return target; + }, + /** + * IE does not return "shadowed" keys even if they are defined directly + * in the object. + * + * @internal + * @type {String[]} + */ + __shadowedKeys : ["isPrototypeOf", "hasOwnProperty", "toLocaleString", "toString", "valueOf", "propertyIsEnumerable", "constructor"], + /** + * Get the keys of a map as array as returned by a "for ... in" statement. + * + * @signature function(map) + * @internal + * @param map {Object} the map + * @return {Array} array of the keys of the map + */ + keys : ({ + "ES5" : Object.keys, + "BROKEN_IE" : function(map){ + + if(map === null || (typeof map != "object" && typeof map != "function")){ + + throw new TypeError("Object.keys requires an object as argument."); + }; + var arr = []; + var hasOwnProperty = Object.prototype.hasOwnProperty; + for(var key in map){ + + if(hasOwnProperty.call(map, key)){ + + arr.push(key); + }; + }; + // IE does not return "shadowed" keys even if they are defined directly + // in the object. This is incompatible with the ECMA standard!! + // This is why this checks are needed. + var shadowedKeys = qx.Bootstrap.__shadowedKeys; + for(var i = 0,a = shadowedKeys,l = a.length;i < l;i++){ + + if(hasOwnProperty.call(map, a[i])){ + + arr.push(a[i]); + }; + }; + return arr; + }, + "default" : function(map){ + + if(map === null || (typeof map != "object" && typeof map != "function")){ + + throw new TypeError("Object.keys requires an object as argument."); + }; + var arr = []; + var hasOwnProperty = Object.prototype.hasOwnProperty; + for(var key in map){ + + if(hasOwnProperty.call(map, key)){ + + arr.push(key); + }; + }; + return arr; + } + })[typeof (Object.keys) == "function" ? "ES5" : (function(){ + + for(var key in { + toString : 1 + }){ + + return key; + }; + })() !== "toString" ? "BROKEN_IE" : "default"], + /** + * Mapping from JavaScript string representation of objects to names + * @internal + * @type {Map} + */ + __classToTypeMap : { + "[object String]" : "String", + "[object Array]" : "Array", + "[object Object]" : "Object", + "[object RegExp]" : "RegExp", + "[object Number]" : "Number", + "[object Boolean]" : "Boolean", + "[object Date]" : "Date", + "[object Function]" : "Function", + "[object Error]" : "Error" + }, + /* + --------------------------------------------------------------------------- + FUNCTION UTILITY FUNCTIONS + --------------------------------------------------------------------------- + */ + /** + * Returns a function whose "this" is altered. + * + * *Syntax* + * + *
qx.Bootstrap.bind(myFunction, [self, [varargs...]]);
+ * + * *Example* + * + *
+     * function myFunction()
+     * {
+     *   this.setStyle('color', 'red');
+     *   // note that 'this' here refers to myFunction, not an element
+     *   // we'll need to bind this function to the element we want to alter
+     * };
+     *
+     * var myBoundFunction = qx.Bootstrap.bind(myFunction, myElement);
+     * myBoundFunction(); // this will make the element myElement red.
+     * 
+ * + * @param func {Function} Original function to wrap + * @param self {Object ? null} The object that the "this" of the function will refer to. + * @param varargs {arguments ? null} The arguments to pass to the function. + * @return {Function} The bound function. + */ + bind : function(func, self, varargs){ + + var fixedArgs = Array.prototype.slice.call(arguments, 2, arguments.length); + return function(){ + + var args = Array.prototype.slice.call(arguments, 0, arguments.length); + return func.apply(self, fixedArgs.concat(args)); + }; + }, + /* + --------------------------------------------------------------------------- + STRING UTILITY FUNCTIONS + --------------------------------------------------------------------------- + */ + /** + * Convert the first character of the string to upper case. + * + * @param str {String} the string + * @return {String} the string with an upper case first character + */ + firstUp : function(str){ + + return str.charAt(0).toUpperCase() + str.substr(1); + }, + /** + * Convert the first character of the string to lower case. + * + * @param str {String} the string + * @return {String} the string with a lower case first character + */ + firstLow : function(str){ + + return str.charAt(0).toLowerCase() + str.substr(1); + }, + /* + --------------------------------------------------------------------------- + TYPE UTILITY FUNCTIONS + --------------------------------------------------------------------------- + */ + /** + * Get the internal class of the value. See + * http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/ + * for details. + * + * @param value {var} value to get the class for + * @return {String} the internal class of the value + */ + getClass : function(value){ + + var classString = Object.prototype.toString.call(value); + return (qx.Bootstrap.__classToTypeMap[classString] || classString.slice(8, -1)); + }, + /** + * Whether the value is a string. + * + * @param value {var} Value to check. + * @return {Boolean} Whether the value is a string. + */ + isString : function(value){ + + // Added "value !== null" because IE throws an exception "Object expected" + // by executing "value instanceof String" if value is a DOM element that + // doesn't exist. It seems that there is an internal difference between a + // JavaScript null and a null returned from calling DOM. + // e.q. by document.getElementById("ReturnedNull"). + return (value !== null && (typeof value === "string" || qx.Bootstrap.getClass(value) == "String" || value instanceof String || (!!value && !!value.$$isString))); + }, + /** + * Whether the value is an array. + * + * @param value {var} Value to check. + * @return {Boolean} Whether the value is an array. + */ + isArray : function(value){ + + // Added "value !== null" because IE throws an exception "Object expected" + // by executing "value instanceof Array" if value is a DOM element that + // doesn't exist. It seems that there is an internal difference between a + // JavaScript null and a null returned from calling DOM. + // e.q. by document.getElementById("ReturnedNull"). + return (value !== null && (value instanceof Array || (value && qx.data && qx.data.IListData && qx.util.OOUtil.hasInterface(value.constructor, qx.data.IListData)) || qx.Bootstrap.getClass(value) == "Array" || (!!value && !!value.$$isArray))); + }, + /** + * Whether the value is an object. Note that built-in types like Window are + * not reported to be objects. + * + * @param value {var} Value to check. + * @return {Boolean} Whether the value is an object. + */ + isObject : function(value){ + + return (value !== undefined && value !== null && qx.Bootstrap.getClass(value) == "Object"); + }, + /** + * Whether the value is a function. + * + * @param value {var} Value to check. + * @return {Boolean} Whether the value is a function. + */ + isFunction : function(value){ + + return qx.Bootstrap.getClass(value) == "Function"; + }, + /* + --------------------------------------------------------------------------- + LOGGING UTILITY FUNCTIONS + --------------------------------------------------------------------------- + */ + $$logs : [], + /** + * Sending a message at level "debug" to the logger. + * + * @param object {Object} Contextual object (either instance or static class) + * @param message {var} Any number of arguments supported. An argument may + * have any JavaScript data type. All data is serialized immediately and + * does not keep references to other objects. + */ + debug : function(object, message){ + + qx.Bootstrap.$$logs.push(["debug", arguments]); + }, + /** + * Sending a message at level "info" to the logger. + * + * @param object {Object} Contextual object (either instance or static class) + * @param message {var} Any number of arguments supported. An argument may + * have any JavaScript data type. All data is serialized immediately and + * does not keep references to other objects. + */ + info : function(object, message){ + + qx.Bootstrap.$$logs.push(["info", arguments]); + }, + /** + * Sending a message at level "warn" to the logger. + * + * @param object {Object} Contextual object (either instance or static class) + * @param message {var} Any number of arguments supported. An argument may + * have any JavaScript data type. All data is serialized immediately and + * does not keep references to other objects. + */ + warn : function(object, message){ + + qx.Bootstrap.$$logs.push(["warn", arguments]); + }, + /** + * Sending a message at level "error" to the logger. + * + * @param object {Object} Contextual object (either instance or static class) + * @param message {var} Any number of arguments supported. An argument may + * have any JavaScript data type. All data is serialized immediately and + * does not keep references to other objects. + */ + error : function(object, message){ + + qx.Bootstrap.$$logs.push(["error", arguments]); + }, + /** + * Prints the current stack trace at level "info" + * + * @param object {Object} Contextual object (either instance or static class) + */ + trace : function(object){ + } + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2011 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Martin Wittemann (wittemann) + +************************************************************************ */ +/** + * This class is a base class for the OO system defined by Class, Mixin + * and Interface. It contains helper which are basically needed to create the + * Classes which define the OO system. + */ +qx.Bootstrap.define("qx.util.OOUtil", { + statics : { + /** + * Whether the given class exists + * + * @param name {String} class name to check + * @return {Boolean} true if class exists + */ + classIsDefined : function(name){ + + return qx.Bootstrap.getByName(name) !== undefined; + }, + /** + * Returns the definition of the given property, if not redefined. + * Returns null if the property does not exist. + * + * @param clazz {Class} class to check + * @param name {String} name of the class to check for + * @return {Map|null} whether the object support the given event. + */ + getPropertyDefinition : function(clazz, name){ + + while(clazz){ + + if(clazz.$$properties && clazz.$$properties[name]){ + + return clazz.$$properties[name]; + }; + clazz = clazz.superclass; + }; + return null; + }, + /** + * Whether a class has the given property + * + * @param clazz {Class} class to check + * @param name {String} name of the property to check for + * @return {Boolean} whether the class includes the given property. + */ + hasProperty : function(clazz, name){ + + return !!qx.util.OOUtil.getPropertyDefinition(clazz, name); + }, + /** + * Returns the event type of the given event. Returns null if + * the event does not exist. + * + * @param clazz {Class} class to check + * @param name {String} name of the event + * @return {String|null} Event type of the given event. + */ + getEventType : function(clazz, name){ + + var clazz = clazz.constructor; + while(clazz.superclass){ + + if(clazz.$$events && clazz.$$events[name] !== undefined){ + + return clazz.$$events[name]; + }; + clazz = clazz.superclass; + }; + return null; + }, + /** + * Whether a class supports the given event type + * + * @param clazz {Class} class to check + * @param name {String} name of the event to check for + * @return {Boolean} whether the class supports the given event. + */ + supportsEvent : function(clazz, name){ + + return !!qx.util.OOUtil.getEventType(clazz, name); + }, + /** + * Returns the class or one of its super classes which contains the + * declaration of the given interface. Returns null if the interface is not + * specified anywhere. + * + * @param clazz {Class} class to look for the interface + * @param iface {Interface} interface to look for + * @return {Class | null} the class which directly implements the given interface + */ + getByInterface : function(clazz, iface){ + + var list,i,l; + while(clazz){ + + if(clazz.$$implements){ + + list = clazz.$$flatImplements; + for(i = 0,l = list.length;i < l;i++){ + + if(list[i] === iface){ + + return clazz; + }; + }; + }; + clazz = clazz.superclass; + }; + return null; + }, + /** + * Whether a given class or any of its super classes includes a given interface. + * + * This function will return "true" if the interface was defined + * in the class declaration ({@link qx.Class#define}) of the class + * or any of its super classes using the "implement" + * key. + * + * @param clazz {Class} class to check + * @param iface {Interface} the interface to check for + * @return {Boolean} whether the class includes the interface. + */ + hasInterface : function(clazz, iface){ + + return !!qx.util.OOUtil.getByInterface(clazz, iface); + }, + /** + * Returns a list of all mixins available in a given class. + * + * @param clazz {Class} class which should be inspected + * @return {Mixin[]} array of mixins this class uses + */ + getMixins : function(clazz){ + + var list = []; + while(clazz){ + + if(clazz.$$includes){ + + list.push.apply(list, clazz.$$flatIncludes); + }; + clazz = clazz.superclass; + }; + return list; + } + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2005-2011 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Martin Wittemann (martinwittemann) + +************************************************************************ */ +/** + * This class is the single point to access all settings that may be different + * in different environments. This contains e.g. the browser name, engine + * version but also qooxdoo or application specific settings. + * + * Its public API can be found in its four main methods. One pair of methods + * is used to check the synchronous values of the environment. The other pair + * of methods is used for asynchronous checks. + * + * The most often used method should be {@link #get}, which returns the + * current value for a given environment check. + * + * All qooxdoo settings can be changed via the generator's config. See the manual + * for more details about the environment key in the config. As you can see + * from the methods API, there is no way to override an existing key. So if you + * need to change a qooxdoo setting, you have to use the generator to do so. + * + * The generator is also responsible for requiring the necessary implementation + * classes for each check. When using a check of a new category, make sure to + * rebuild you application and let the generator include the necessary files. + * + * The following table shows the available checks. If you are + * interested in more details, check the reference to the implementation of + * each check. Please do not use those check implementations directly, as the + * Environment class comes with a smart caching feature. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * +* +* +* +* + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *

Synchronous checks

+ *

Key

Type

Example

Details

browser
browser.documentmodeInteger0{@link qx.bom.client.Browser#getDocumentMode}
browser.nameString chrome {@link qx.bom.client.Browser#getName}
browser.quirksmodeBooleanfalse{@link qx.bom.client.Browser#getQuirksMode}
browser.versionString11.0{@link qx.bom.client.Browser#getVersion}
runtime
runtime.name String node.js {@link qx.bom.client.Runtime#getName}
css
css.borderradiusString or nullborderRadius{@link qx.bom.client.Css#getBorderRadius}
css.borderimageString or nullWebkitBorderImage{@link qx.bom.client.Css#getBorderImage}
css.borderimage.standardsyntaxBoolean or nulltrue{@link qx.bom.client.Css#getBorderImageSyntax}
css.boxmodelStringcontent{@link qx.bom.client.Css#getBoxModel}
css.boxshadowString or nullboxShadow{@link qx.bom.client.Css#getBoxShadow}
css.gradient.linearString or null-moz-linear-gradient{@link qx.bom.client.Css#getLinearGradient}
css.gradient.filterBooleantrue{@link qx.bom.client.Css#getFilterGradient}
css.gradient.radialString or null-moz-radial-gradient{@link qx.bom.client.Css#getRadialGradient}
css.gradient.legacywebkitBooleanfalse{@link qx.bom.client.Css#getLegacyWebkitGradient}
css.placeholderBooleantrue{@link qx.bom.client.Css#getPlaceholder}
css.textoverflowString or nulltextOverflow{@link qx.bom.client.Css#getTextOverflow}
css.rgbaBooleantrue{@link qx.bom.client.Css#getRgba}
css.usermodifyString or nullWebkitUserModify{@link qx.bom.client.Css#getUserModify}
css.appearanceString or nullWebkitAppearance{@link qx.bom.client.Css#getAppearance}
css.floatString or nullcssFloat{@link qx.bom.client.Css#getFloat}
css.userselectString or nullWebkitUserSelect{@link qx.bom.client.Css#getUserSelect}
css.userselect.noneString or null-moz-none{@link qx.bom.client.Css#getUserSelectNone}
css.boxsizingString or nullboxSizing{@link qx.bom.client.Css#getBoxSizing}
css.animationObject or null{end-event: "webkitAnimationEnd", keyframes: "@-webkit-keyframes", play-state: null, name: "WebkitAnimation"}{@link qx.bom.client.CssAnimation#getSupport}
css.animation.requestframeString or nullmozRequestAnimationFrame{@link qx.bom.client.CssAnimation#getRequestAnimationFrame}
css.transformObject or null{3d: true, origin: "WebkitTransformOrigin", name: "WebkitTransform", style: "WebkitTransformStyle", perspective: "WebkitPerspective", perspective-origin: "WebkitPerspectiveOrigin", backface-visibility: "WebkitBackfaceVisibility"}{@link qx.bom.client.CssTransform#getSupport}
css.transform.3dBooleanfalse{@link qx.bom.client.CssTransform#get3D}
css.transitionObject or null{end-event: "webkitTransitionEnd", name: "WebkitTransition"}{@link qx.bom.client.CssTransition#getSupport}
css.inlineblockString or nullinline-block{@link qx.bom.client.Css#getInlineBlock}
css.opacityBooleantrue{@link qx.bom.client.Css#getOpacity}
css.textShadowBooleantrue{@link qx.bom.client.Css#getTextShadow}
css.textShadow.filterBooleantrue{@link qx.bom.client.Css#getFilterTextShadow}
css.alphaimageloaderneededBooleanfalse{@link qx.bom.client.Css#getAlphaImageLoaderNeeded}
css.pointereventsBooleantrue{@link qx.bom.client.Css#getPointerEvents}
css.flexboxSyntaxString or null"flex"{@link qx.bom.client.Css#getFlexboxSyntax}
device
device.nameStringpc{@link qx.bom.client.Device#getName}
device.typeStringmobile{@link qx.bom.client.Device#getType}
device.pixelRatioNumber2{@link qx.bom.client.Device#getDevicePixelRatio}
device.touchStringtrue{@link qx.bom.client.Device#getTouch}
ecmascript
ecmascript.error.stacktraceString or nullstack{@link qx.bom.client.EcmaScript#getStackTrace}
ecmascript.array.indexofBooleantrue{@link qx.bom.client.EcmaScript#getArrayIndexOf}
ecmascript.array.lastindexofBooleantrue{@link qx.bom.client.EcmaScript#getArrayLastIndexOf}
ecmascript.array.foreachBooleantrue{@link qx.bom.client.EcmaScript#getArrayForEach}
ecmascript.array.filterBooleantrue{@link qx.bom.client.EcmaScript#getArrayFilter}
ecmascript.array.mapBooleantrue{@link qx.bom.client.EcmaScript#getArrayMap}
ecmascript.array.someBooleantrue{@link qx.bom.client.EcmaScript#getArraySome}
ecmascript.array.everyBooleantrue{@link qx.bom.client.EcmaScript#getArrayEvery}
ecmascript.array.reduceBooleantrue{@link qx.bom.client.EcmaScript#getArrayReduce}
ecmascript.array.reducerightBooleantrue{@link qx.bom.client.EcmaScript#getArrayReduceRight}
ecmascript.function.bindBooleantrue{@link qx.bom.client.EcmaScript#getFunctionBind}
ecmascript.object.keysBooleantrue{@link qx.bom.client.EcmaScript#getObjectKeys}
ecmascript.date.nowBooleantrue{@link qx.bom.client.EcmaScript#getDateNow}
ecmascript.error.toStringBooleantrue{@link qx.bom.client.EcmaScript#getErrorToString}
ecmascript.string.trimBooleantrue{@link qx.bom.client.EcmaScript#getStringTrim}
engine
engine.nameStringwebkit{@link qx.bom.client.Engine#getName}
engine.versionString534.24{@link qx.bom.client.Engine#getVersion}
event
event.mspointerBooleantrue{@link qx.bom.client.Event#getMsPointer}
event.touchBooleanfalse{@link qx.bom.client.Event#getTouch}
event.helpBooleanfalse{@link qx.bom.client.Event#getHelp}
event.hashchangeBooleantrue{@link qx.bom.client.Event#getHashChange}
event.dispatcheventBooleantrue{@link qx.bom.client.Event#getDispatchEvent}
event.customeventBooleantrue{@link qx.bom.client.Event#getCustomEvent}
event.mouseeventBooleantrue{@link qx.bom.client.Event#getMouseEvent}
event.mousewheelMap{type: "wheel", target: window}{@link qx.bom.client.Event#getMouseWheel}
html
html.audioBooleantrue{@link qx.bom.client.Html#getAudio}
html.audio.mp3String""{@link qx.bom.client.Html#getAudioMp3}
html.audio.oggString"maybe"{@link qx.bom.client.Html#getAudioOgg}
html.audio.wavString"probably"{@link qx.bom.client.Html#getAudioWav}
html.audio.auString"maybe"{@link qx.bom.client.Html#getAudioAu}
html.audio.aifString"probably"{@link qx.bom.client.Html#getAudioAif}
html.canvasBooleantrue{@link qx.bom.client.Html#getCanvas}
html.classlistBooleantrue{@link qx.bom.client.Html#getClassList}
html.geolocationBooleantrue{@link qx.bom.client.Html#getGeoLocation}
html.storage.localBooleantrue{@link qx.bom.client.Html#getLocalStorage}
html.storage.sessionBooleantrue{@link qx.bom.client.Html#getSessionStorage}
html.storage.userdataBooleantrue{@link qx.bom.client.Html#getUserDataStorage}
html.svgBooleantrue{@link qx.bom.client.Html#getSvg}
html.videoBooleantrue{@link qx.bom.client.Html#getVideo}
html.video.h264String"probably"{@link qx.bom.client.Html#getVideoH264}
html.video.oggString""{@link qx.bom.client.Html#getVideoOgg}
html.video.webmString"maybe"{@link qx.bom.client.Html#getVideoWebm}
html.vmlBooleanfalse{@link qx.bom.client.Html#getVml}
html.webworkerBooleantrue{@link qx.bom.client.Html#getWebWorker}
html.filereaderBooleantrue{@link qx.bom.client.Html#getFileReader}
html.xpathBooleantrue{@link qx.bom.client.Html#getXPath}
html.xulBooleantrue{@link qx.bom.client.Html#getXul}
html.consoleBooleantrue{@link qx.bom.client.Html#getConsole}
html.element.containsBooleantrue{@link qx.bom.client.Html#getContains}
html.element.compareDocumentPositionBooleantrue{@link qx.bom.client.Html#getCompareDocumentPosition}
html.element.textContentBooleantrue{@link qx.bom.client.Html#getTextContent}
html.image.naturaldimensionsBooleantrue{@link qx.bom.client.Html#getNaturalDimensions}
html.history.stateBooleantrue{@link qx.bom.client.Html#getHistoryState}
html.selectionStringgetSelection{@link qx.bom.client.Html#getSelection}
html.node.isequalnodeBooleantrue{@link qx.bom.client.Html#getIsEqualNode}
XML
xml.implementationBooleantrue{@link qx.bom.client.Xml#getImplementation}
xml.domparserBooleantrue{@link qx.bom.client.Xml#getDomParser}
xml.selectsinglenodeBooleanfalse{@link qx.bom.client.Xml#getSelectSingleNode}
xml.selectnodesBooleanfalse{@link qx.bom.client.Xml#getSelectNodes}
xml.getelementsbytagnamensBooleantrue{@link qx.bom.client.Xml#getElementsByTagNameNS}
xml.dompropertiesBooleanfalse{@link qx.bom.client.Xml#getDomProperties}
xml.attributensBooleantrue{@link qx.bom.client.Xml#getAttributeNS}
xml.createelementnsBooleantrue{@link qx.bom.client.Xml#getCreateElementNS}
xml.createnodeBooleanfalse{@link qx.bom.client.Xml#getCreateNode}
xml.getqualifieditemBooleanfalse{@link qx.bom.client.Xml#getQualifiedItem}
Stylesheets
html.stylesheet.createstylesheetBooleanfalse{@link qx.bom.client.Stylesheet#getCreateStyleSheet}
html.stylesheet.insertruleBooleantrue{@link qx.bom.client.Stylesheet#getInsertRule}
html.stylesheet.deleteruleBooleantrue{@link qx.bom.client.Stylesheet#getDeleteRule}
html.stylesheet.addimportBooleanfalse{@link qx.bom.client.Stylesheet#getAddImport}
html.stylesheet.removeimportBooleanfalse{@link qx.bom.client.Stylesheet#getRemoveImport}
io
io.maxrequestsInteger4{@link qx.bom.client.Transport#getMaxConcurrentRequestCount}
io.sslBooleanfalse{@link qx.bom.client.Transport#getSsl}
io.xhrStringxhr{@link qx.bom.client.Transport#getXmlHttpRequest}
locale
localeStringde{@link qx.bom.client.Locale#getLocale}
locale.variantStringde{@link qx.bom.client.Locale#getVariant}
os
os.nameStringosx{@link qx.bom.client.OperatingSystem#getName}
os.versionString10.6{@link qx.bom.client.OperatingSystem#getVersion}
os.scrollBarOverlayedBooleanfalse{@link qx.bom.client.Scroll#scrollBarOverlayed}
phonegap
phonegapBooleanfalse{@link qx.bom.client.PhoneGap#getPhoneGap}
phonegap.notificationBooleanfalse{@link qx.bom.client.PhoneGap#getNotification}
plugin
plugin.divxBooleanfalse{@link qx.bom.client.Plugin#getDivX}
plugin.divx.versionString{@link qx.bom.client.Plugin#getDivXVersion}
plugin.flashBooleantrue{@link qx.bom.client.Flash#isAvailable}
plugin.flash.expressBooleantrue{@link qx.bom.client.Flash#getExpressInstall}
plugin.flash.strictsecurityBooleantrue{@link qx.bom.client.Flash#getStrictSecurityModel}
plugin.flash.versionString10.2.154{@link qx.bom.client.Flash#getVersion}
plugin.gearsBooleanfalse{@link qx.bom.client.Plugin#getGears}
plugin.activexBooleanfalse{@link qx.bom.client.Plugin#getActiveX}
plugin.skypeBooleanfalse{@link qx.bom.client.Plugin#getSkype}
plugin.pdfBooleanfalse{@link qx.bom.client.Plugin#getPdf}
plugin.pdf.versionString{@link qx.bom.client.Plugin#getPdfVersion}
plugin.quicktimeBooleantrue{@link qx.bom.client.Plugin#getQuicktime}
plugin.quicktime.versionString7.6{@link qx.bom.client.Plugin#getQuicktimeVersion}
plugin.silverlightBooleanfalse{@link qx.bom.client.Plugin#getSilverlight}
plugin.silverlight.versionString{@link qx.bom.client.Plugin#getSilverlightVersion}
plugin.windowsmediaBooleanfalse{@link qx.bom.client.Plugin#getWindowsMedia}
plugin.windowsmedia.versionString{@link qx.bom.client.Plugin#getWindowsMediaVersion}
qx
qx.allowUrlSettingsBooleantruedefault: false
qx.allowUrlVariantsBooleantruedefault: false
qx.applicationStringname.spacedefault: <<application name>>
qx.aspectsBooleanfalsedefault: false
qx.debugBooleantruedefault: true
qx.debug.databindingBooleanfalsedefault: false
qx.debug.disposeBooleanfalsedefault: false
qx.debug.dispose.levelInteger0default: 0
qx.debug.ioBooleantruedefault: false
qx.debug.io.remoteBooleantruedefault: false
qx.debug.io.remote.dataBooleantruedefault: false
qx.debug.property.levelInteger0default: 0
qx.debug.ui.queueBooleantruedefault: true
qx.dynlocaleBooleantruedefault: true
qx.dynthemeBooleantruedefault: true
qx.globalErrorHandlingBooleantruedefault: true
qx.mobile.nativescrollBooleanfalse{@link qx.bom.client.Scroll#getNativeScroll}
qx.optimization.basecallsBooleantruetrue if the corresp. optimize key is set in the config
qx.optimization.commentsBooleantruetrue if the corresp. optimize key is set in the config
qx.optimization.privatesBooleantruetrue if the corresp. optimize key is set in the config
qx.optimization.stringsBooleantruetrue if the corresp. optimize key is set in the config
qx.optimization.variablesBooleantruetrue if the corresp. optimize key is set in the config
qx.optimization.variantsBooleantruetrue if the corresp. optimize key is set in the config
qx.revisionString27348
qx.themeStringqx.theme.Moderndefault: <<initial theme name>>
qx.versionString${qxversion}
qx.blankpageStringURI to blank.html page
module
module.databindingBooleantruedefault: true
module.loggerBooleantruedefault: true
module.propertyBooleantruedefault: true
module.eventsBooleantruedefault: true

Asynchronous checks

+ *
html.dataurlBooleantrue{@link qx.bom.client.Html#getDataUrl}
plugin.pdfjsBooleanfalse{@link qx.bom.client.Pdfjs#getPdfjs}
+ * + */ +qx.Bootstrap.define("qx.core.Environment", { + statics : { + /** Map containing the synchronous check functions. */ + _checks : { + }, + /** Map containing the asynchronous check functions. */ + _asyncChecks : { + }, + /** Internal cache for all checks. */ + __cache : { + }, + /** Internal map for environment keys to check methods. */ + _checksMap : { + "engine.version" : "qx.bom.client.Engine.getVersion", + "engine.name" : "qx.bom.client.Engine.getName", + "browser.name" : "qx.bom.client.Browser.getName", + "browser.version" : "qx.bom.client.Browser.getVersion", + "browser.documentmode" : "qx.bom.client.Browser.getDocumentMode", + "browser.quirksmode" : "qx.bom.client.Browser.getQuirksMode", + "runtime.name" : "qx.bom.client.Runtime.getName", + "device.name" : "qx.bom.client.Device.getName", + "device.type" : "qx.bom.client.Device.getType", + "device.pixelRatio" : "qx.bom.client.Device.getPixelRatio", + "device.touch" : "qx.bom.client.Device.getTouch", + "locale" : "qx.bom.client.Locale.getLocale", + "locale.variant" : "qx.bom.client.Locale.getVariant", + "os.name" : "qx.bom.client.OperatingSystem.getName", + "os.version" : "qx.bom.client.OperatingSystem.getVersion", + "os.scrollBarOverlayed" : "qx.bom.client.Scroll.scrollBarOverlayed", + "plugin.gears" : "qx.bom.client.Plugin.getGears", + "plugin.activex" : "qx.bom.client.Plugin.getActiveX", + "plugin.skype" : "qx.bom.client.Plugin.getSkype", + "plugin.quicktime" : "qx.bom.client.Plugin.getQuicktime", + "plugin.quicktime.version" : "qx.bom.client.Plugin.getQuicktimeVersion", + "plugin.windowsmedia" : "qx.bom.client.Plugin.getWindowsMedia", + "plugin.windowsmedia.version" : "qx.bom.client.Plugin.getWindowsMediaVersion", + "plugin.divx" : "qx.bom.client.Plugin.getDivX", + "plugin.divx.version" : "qx.bom.client.Plugin.getDivXVersion", + "plugin.silverlight" : "qx.bom.client.Plugin.getSilverlight", + "plugin.silverlight.version" : "qx.bom.client.Plugin.getSilverlightVersion", + "plugin.flash" : "qx.bom.client.Flash.isAvailable", + "plugin.flash.version" : "qx.bom.client.Flash.getVersion", + "plugin.flash.express" : "qx.bom.client.Flash.getExpressInstall", + "plugin.flash.strictsecurity" : "qx.bom.client.Flash.getStrictSecurityModel", + "plugin.pdf" : "qx.bom.client.Plugin.getPdf", + "plugin.pdf.version" : "qx.bom.client.Plugin.getPdfVersion", + "plugin.pdfjs" : "qx.bom.client.Pdfjs.getPdfjs", + "io.maxrequests" : "qx.bom.client.Transport.getMaxConcurrentRequestCount", + "io.ssl" : "qx.bom.client.Transport.getSsl", + "io.xhr" : "qx.bom.client.Transport.getXmlHttpRequest", + "event.touch" : "qx.bom.client.Event.getTouch", + "event.mspointer" : "qx.bom.client.Event.getMsPointer", + "event.help" : "qx.bom.client.Event.getHelp", + "event.hashchange" : "qx.bom.client.Event.getHashChange", + "event.dispatchevent" : "qx.bom.client.Event.getDispatchEvent", + "event.customevent" : "qx.bom.client.Event.getCustomEvent", + "event.mouseevent" : "qx.bom.client.Event.getMouseEvent", + "event.mousewheel" : "qx.bom.client.Event.getMouseWheel", + "ecmascript.error.stacktrace" : "qx.bom.client.EcmaScript.getStackTrace", + "ecmascript.array.indexof" : "qx.bom.client.EcmaScript.getArrayIndexOf", + "ecmascript.array.lastindexof" : "qx.bom.client.EcmaScript.getArrayLastIndexOf", + "ecmascript.array.foreach" : "qx.bom.client.EcmaScript.getArrayForEach", + "ecmascript.array.filter" : "qx.bom.client.EcmaScript.getArrayFilter", + "ecmascript.array.map" : "qx.bom.client.EcmaScript.getArrayMap", + "ecmascript.array.some" : "qx.bom.client.EcmaScript.getArraySome", + "ecmascript.array.every" : "qx.bom.client.EcmaScript.getArrayEvery", + "ecmascript.array.reduce" : "qx.bom.client.EcmaScript.getArrayReduce", + "ecmascript.array.reduceright" : "qx.bom.client.EcmaScript.getArrayReduceRight", + "ecmascript.function.bind" : "qx.bom.client.EcmaScript.getFunctionBind", + "ecmascript.object.keys" : "qx.bom.client.EcmaScript.getObjectKeys", + "ecmascript.date.now" : "qx.bom.client.EcmaScript.getDateNow", + "ecmascript.error.toString" : "qx.bom.client.EcmaScript.getErrorToString", + "ecmascript.string.trim" : "qx.bom.client.EcmaScript.getStringTrim", + "html.webworker" : "qx.bom.client.Html.getWebWorker", + "html.filereader" : "qx.bom.client.Html.getFileReader", + "html.geolocation" : "qx.bom.client.Html.getGeoLocation", + "html.audio" : "qx.bom.client.Html.getAudio", + "html.audio.ogg" : "qx.bom.client.Html.getAudioOgg", + "html.audio.mp3" : "qx.bom.client.Html.getAudioMp3", + "html.audio.wav" : "qx.bom.client.Html.getAudioWav", + "html.audio.au" : "qx.bom.client.Html.getAudioAu", + "html.audio.aif" : "qx.bom.client.Html.getAudioAif", + "html.video" : "qx.bom.client.Html.getVideo", + "html.video.ogg" : "qx.bom.client.Html.getVideoOgg", + "html.video.h264" : "qx.bom.client.Html.getVideoH264", + "html.video.webm" : "qx.bom.client.Html.getVideoWebm", + "html.storage.local" : "qx.bom.client.Html.getLocalStorage", + "html.storage.session" : "qx.bom.client.Html.getSessionStorage", + "html.storage.userdata" : "qx.bom.client.Html.getUserDataStorage", + "html.classlist" : "qx.bom.client.Html.getClassList", + "html.xpath" : "qx.bom.client.Html.getXPath", + "html.xul" : "qx.bom.client.Html.getXul", + "html.canvas" : "qx.bom.client.Html.getCanvas", + "html.svg" : "qx.bom.client.Html.getSvg", + "html.vml" : "qx.bom.client.Html.getVml", + "html.dataset" : "qx.bom.client.Html.getDataset", + "html.dataurl" : "qx.bom.client.Html.getDataUrl", + "html.console" : "qx.bom.client.Html.getConsole", + "html.stylesheet.createstylesheet" : "qx.bom.client.Stylesheet.getCreateStyleSheet", + "html.stylesheet.insertrule" : "qx.bom.client.Stylesheet.getInsertRule", + "html.stylesheet.deleterule" : "qx.bom.client.Stylesheet.getDeleteRule", + "html.stylesheet.addimport" : "qx.bom.client.Stylesheet.getAddImport", + "html.stylesheet.removeimport" : "qx.bom.client.Stylesheet.getRemoveImport", + "html.element.contains" : "qx.bom.client.Html.getContains", + "html.element.compareDocumentPosition" : "qx.bom.client.Html.getCompareDocumentPosition", + "html.element.textcontent" : "qx.bom.client.Html.getTextContent", + "html.image.naturaldimensions" : "qx.bom.client.Html.getNaturalDimensions", + "html.history.state" : "qx.bom.client.Html.getHistoryState", + "html.selection" : "qx.bom.client.Html.getSelection", + "html.node.isequalnode" : "qx.bom.client.Html.getIsEqualNode", + "json" : "qx.bom.client.Json.getJson", + "css.textoverflow" : "qx.bom.client.Css.getTextOverflow", + "css.placeholder" : "qx.bom.client.Css.getPlaceholder", + "css.borderradius" : "qx.bom.client.Css.getBorderRadius", + "css.borderimage" : "qx.bom.client.Css.getBorderImage", + "css.borderimage.standardsyntax" : "qx.bom.client.Css.getBorderImageSyntax", + "css.boxshadow" : "qx.bom.client.Css.getBoxShadow", + "css.gradient.linear" : "qx.bom.client.Css.getLinearGradient", + "css.gradient.filter" : "qx.bom.client.Css.getFilterGradient", + "css.gradient.radial" : "qx.bom.client.Css.getRadialGradient", + "css.gradient.legacywebkit" : "qx.bom.client.Css.getLegacyWebkitGradient", + "css.boxmodel" : "qx.bom.client.Css.getBoxModel", + "css.rgba" : "qx.bom.client.Css.getRgba", + "css.userselect" : "qx.bom.client.Css.getUserSelect", + "css.userselect.none" : "qx.bom.client.Css.getUserSelectNone", + "css.usermodify" : "qx.bom.client.Css.getUserModify", + "css.appearance" : "qx.bom.client.Css.getAppearance", + "css.float" : "qx.bom.client.Css.getFloat", + "css.boxsizing" : "qx.bom.client.Css.getBoxSizing", + "css.animation" : "qx.bom.client.CssAnimation.getSupport", + "css.animation.requestframe" : "qx.bom.client.CssAnimation.getRequestAnimationFrame", + "css.transform" : "qx.bom.client.CssTransform.getSupport", + "css.transform.3d" : "qx.bom.client.CssTransform.get3D", + "css.transition" : "qx.bom.client.CssTransition.getSupport", + "css.inlineblock" : "qx.bom.client.Css.getInlineBlock", + "css.opacity" : "qx.bom.client.Css.getOpacity", + "css.textShadow" : "qx.bom.client.Css.getTextShadow", + "css.textShadow.filter" : "qx.bom.client.Css.getFilterTextShadow", + "css.alphaimageloaderneeded" : "qx.bom.client.Css.getAlphaImageLoaderNeeded", + "css.pointerevents" : "qx.bom.client.Css.getPointerEvents", + "phonegap" : "qx.bom.client.PhoneGap.getPhoneGap", + "phonegap.notification" : "qx.bom.client.PhoneGap.getNotification", + "xml.implementation" : "qx.bom.client.Xml.getImplementation", + "xml.domparser" : "qx.bom.client.Xml.getDomParser", + "xml.selectsinglenode" : "qx.bom.client.Xml.getSelectSingleNode", + "xml.selectnodes" : "qx.bom.client.Xml.getSelectNodes", + "xml.getelementsbytagnamens" : "qx.bom.client.Xml.getElementsByTagNameNS", + "xml.domproperties" : "qx.bom.client.Xml.getDomProperties", + "xml.attributens" : "qx.bom.client.Xml.getAttributeNS", + "xml.createnode" : "qx.bom.client.Xml.getCreateNode", + "xml.getqualifieditem" : "qx.bom.client.Xml.getQualifiedItem", + "xml.createelementns" : "qx.bom.client.Xml.getCreateElementNS", + "qx.mobile.nativescroll" : "qx.bom.client.Scroll.getNativeScroll" + }, + /** + * The default accessor for the checks. It returns the value the current + * environment has for the given key. The key could be something like + * "qx.debug", "css.textoverflow" or "io.ssl". A complete list of + * checks can be found in the class comment of this class. + * + * Please keep in mind that the result is cached. If you want to run the + * check function again in case something could have been changed, take a + * look at the {@link #invalidateCacheKey} function. + * + * @param key {String} The name of the check you want to query. + * @return {var} The stored value depending on the given key. + * (Details in the class doc) + */ + get : function(key){ + + if(qx.Bootstrap.DEBUG){ + + // @deprecated {3.5} + if(key === "json"){ + + qx.Bootstrap.warn("The environment key 'json' is deprecated " + "and will eventually be removed."); + }; + // @deprecated {4.0} + if(key === "qx.emulatemouse"){ + + qx.Bootstrap.warn("The environment key 'qx.emulatemouse' has been removed. " + "See the release notes for more details."); + }; + // @deprecated {4.0} + if(key === "qx.mobile.emulatetouch"){ + + qx.Bootstrap.warn("The environment key 'qx.mobile.emulatetouch' has been removed. " + "See the release notes for more details."); + }; + }; + // check the cache + if(this.__cache[key] != undefined){ + + return this.__cache[key]; + }; + // search for a matching check + var check = this._checks[key]; + if(check){ + + // execute the check and write the result in the cache + var value = check(); + this.__cache[key] = value; + return value; + }; + // try class lookup + var classAndMethod = this._getClassNameFromEnvKey(key); + if(classAndMethod[0] != undefined){ + + var clazz = classAndMethod[0]; + var method = classAndMethod[1]; + var value = clazz[method](); + // call the check method + this.__cache[key] = value; + return value; + }; + // debug flag + if(qx.Bootstrap.DEBUG){ + + qx.Bootstrap.warn(key + " is not a valid key. Please see the API-doc of " + "qx.core.Environment for a list of predefined keys."); + qx.Bootstrap.trace(this); + }; + }, + /** + * Maps an environment key to a check class and method name. + * + * @param key {String} The name of the check you want to query. + * @return {Array} [className, methodName] of + * the corresponding implementation. + */ + _getClassNameFromEnvKey : function(key){ + + var envmappings = this._checksMap; + if(envmappings[key] != undefined){ + + var implementation = envmappings[key]; + // separate class from method + var lastdot = implementation.lastIndexOf("."); + if(lastdot > -1){ + + var classname = implementation.slice(0, lastdot); + var methodname = implementation.slice(lastdot + 1); + var clazz = qx.Bootstrap.getByName(classname); + if(clazz != undefined){ + + return [clazz, methodname]; + }; + }; + }; + return [undefined, undefined]; + }, + /** + * Invokes the callback as soon as the check has been done. If no check + * could be found, a warning will be printed. + * + * @param key {String} The key of the asynchronous check. + * @param callback {Function} The function to call as soon as the check is + * done. The function should have one argument which is the result of the + * check. + * @param self {var} The context to use when invoking the callback. + */ + getAsync : function(key, callback, self){ + + // check the cache + var env = this; + if(this.__cache[key] != undefined){ + + // force async behavior + window.setTimeout(function(){ + + callback.call(self, env.__cache[key]); + }, 0); + return; + }; + var check = this._asyncChecks[key]; + if(check){ + + check(function(result){ + + env.__cache[key] = result; + callback.call(self, result); + }); + return; + }; + // try class lookup + var classAndMethod = this._getClassNameFromEnvKey(key); + if(classAndMethod[0] != undefined){ + + var clazz = classAndMethod[0]; + var method = classAndMethod[1]; + clazz[method](function(result){ + + // call the check method + env.__cache[key] = result; + callback.call(self, result); + }); + return; + }; + // debug flag + if(qx.Bootstrap.DEBUG){ + + qx.Bootstrap.warn(key + " is not a valid key. Please see the API-doc of " + "qx.core.Environment for a list of predefined keys."); + qx.Bootstrap.trace(this); + }; + }, + /** + * Returns the proper value dependent on the check for the given key. + * + * @param key {String} The name of the check the select depends on. + * @param values {Map} A map containing the values which should be returned + * in any case. The "default" key could be used as a catch all statement. + * @return {var} The value which is stored in the map for the given + * check of the key. + */ + select : function(key, values){ + + return this.__pickFromValues(this.get(key), values); + }, + /** + * Selects the proper function dependent on the asynchronous check. + * + * @param key {String} The key for the async check. + * @param values {Map} A map containing functions. The map keys should + * contain all possibilities which could be returned by the given check + * key. The "default" key could be used as a catch all statement. + * The called function will get one parameter, the result of the query. + * @param self {var} The context which should be used when calling the + * method in the values map. + */ + selectAsync : function(key, values, self){ + + this.getAsync(key, function(result){ + + var value = this.__pickFromValues(key, values); + value.call(self, result); + }, this); + }, + /** + * Internal helper which tries to pick the given key from the given values + * map. If that key is not found, it tries to use a key named "default". + * If there is also no default key, it prints out a warning and returns + * undefined. + * + * @param key {String} The key to search for in the values. + * @param values {Map} A map containing some keys. + * @return {var} The value stored as values[key] usually. + */ + __pickFromValues : function(key, values){ + + var value = values[key]; + if(values.hasOwnProperty(key)){ + + return value; + }; + // check for piped values + for(var id in values){ + + if(id.indexOf("|") != -1){ + + var ids = id.split("|"); + for(var i = 0;i < ids.length;i++){ + + if(ids[i] == key){ + + return values[id]; + }; + }; + }; + }; + if(values["default"] !== undefined){ + + return values["default"]; + }; + if(qx.Bootstrap.DEBUG){ + + throw new Error('No match for variant "' + key + '" (' + (typeof key) + ' type)' + ' in variants [' + qx.Bootstrap.keys(values) + '] found, and no default ("default") given'); + }; + }, + /** + * Takes a given map containing the check names as keys and converts + * the map to an array only containing the values for check evaluating + * to true. This is especially handy for conditional + * includes of mixins. + * @param map {Map} A map containing check names as keys and values. + * @return {Array} An array containing the values. + */ + filter : function(map){ + + var returnArray = []; + for(var check in map){ + + if(this.get(check)){ + + returnArray.push(map[check]); + }; + }; + return returnArray; + }, + /** + * Invalidates the cache for the given key. + * + * @param key {String} The key of the check. + */ + invalidateCacheKey : function(key){ + + delete this.__cache[key]; + }, + /** + * Add a check to the environment class. If there is already a check + * added for the given key, the add will be ignored. + * + * @param key {String} The key for the check e.g. html.featurexyz. + * @param check {var} It could be either a function or a simple value. + * The function should be responsible for the check and should return the + * result of the check. + */ + add : function(key, check){ + + // ignore already added checks. + if(this._checks[key] == undefined){ + + // add functions directly + if(check instanceof Function){ + + this._checks[key] = check; + } else { + + this._checks[key] = this.__createCheck(check); + }; + }; + }, + /** + * Adds an asynchronous check to the environment. If there is already a check + * added for the given key, the add will be ignored. + * + * @param key {String} The key of the check e.g. html.featureabc + * @param check {Function} A function which should check for a specific + * environment setting in an asynchronous way. The method should take two + * arguments. First one is the callback and the second one is the context. + */ + addAsync : function(key, check){ + + if(this._checks[key] == undefined){ + + this._asyncChecks[key] = check; + }; + }, + /** + * Returns all currently defined synchronous checks. + * + * @internal + * @return {Map} The map of synchronous checks + */ + getChecks : function(){ + + return this._checks; + }, + /** + * Returns all currently defined asynchronous checks. + * + * @internal + * @return {Map} The map of asynchronous checks + */ + getAsyncChecks : function(){ + + return this._asyncChecks; + }, + /** + * Initializer for the default values of the framework settings. + */ + _initDefaultQxValues : function(){ + + // an always-true key (e.g. for use in qx.core.Environment.filter() calls) + this.add("true", function(){ + + return true; + }); + // old settings + this.add("qx.allowUrlSettings", function(){ + + return false; + }); + this.add("qx.allowUrlVariants", function(){ + + return false; + }); + this.add("qx.debug.property.level", function(){ + + return 0; + }); + // old variants + // make sure to reflect all changes to qx.debug here in the bootstrap class! + this.add("qx.debug", function(){ + + return true; + }); + this.add("qx.debug.ui.queue", function(){ + + return true; + }); + this.add("qx.aspects", function(){ + + return false; + }); + this.add("qx.dynlocale", function(){ + + return true; + }); + this.add("qx.dyntheme", function(){ + + return true; + }); + this.add("qx.mobile.emulatetouch", function(){ + + return false; + }); + // @deprecated {4.0} + this.add("qx.emulatemouse", function(){ + + return false; + }); + // @deprecated {4.0} + this.add("qx.blankpage", function(){ + + return "qx/static/blank.html"; + }); + this.add("qx.debug.databinding", function(){ + + return false; + }); + this.add("qx.debug.dispose", function(){ + + return false; + }); + // generator optimization vectors + this.add("qx.optimization.basecalls", function(){ + + return false; + }); + this.add("qx.optimization.comments", function(){ + + return false; + }); + this.add("qx.optimization.privates", function(){ + + return false; + }); + this.add("qx.optimization.strings", function(){ + + return false; + }); + this.add("qx.optimization.variables", function(){ + + return false; + }); + this.add("qx.optimization.variants", function(){ + + return false; + }); + // qooxdoo modules + this.add("module.databinding", function(){ + + return true; + }); + this.add("module.logger", function(){ + + return true; + }); + this.add("module.property", function(){ + + return true; + }); + this.add("module.events", function(){ + + return true; + }); + this.add("qx.nativeScrollBars", function(){ + + return false; + }); + }, + /** + * Import checks from global qx.$$environment into the Environment class. + */ + __importFromGenerator : function(){ + + // import the environment map + if(qx && qx.$$environment){ + + for(var key in qx.$$environment){ + + var value = qx.$$environment[key]; + this._checks[key] = this.__createCheck(value); + }; + }; + }, + /** + * Checks the URL for environment settings and imports these into the + * Environment class. + */ + __importFromUrl : function(){ + + if(window.document && window.document.location){ + + var urlChecks = window.document.location.search.slice(1).split("&"); + for(var i = 0;i < urlChecks.length;i++){ + + var check = urlChecks[i].split(":"); + if(check.length != 3 || check[0] != "qxenv"){ + + continue; + }; + var key = check[1]; + var value = decodeURIComponent(check[2]); + // implicit type conversion + if(value == "true"){ + + value = true; + } else if(value == "false"){ + + value = false; + } else if(/^(\d|\.)+$/.test(value)){ + + value = parseFloat(value); + };; + this._checks[key] = this.__createCheck(value); + }; + }; + }, + /** + * Internal helper which creates a function returning the given value. + * + * @param value {var} The value which should be returned. + * @return {Function} A function which could be used by a test. + */ + __createCheck : function(value){ + + return qx.Bootstrap.bind(function(value){ + + return value; + }, null, value); + } + }, + defer : function(statics){ + + // create default values for the environment class + statics._initDefaultQxValues(); + // load the checks from the generator + statics.__importFromGenerator(); + // load the checks from the url + if(statics.get("qx.allowUrlSettings") === true){ + + statics.__importFromUrl(); + }; + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Sebastian Werner (wpbasti) + * Martin Wittemann (martinwittemann) + + ====================================================================== + + This class contains code from: + + Copyright: + 2011 Pocket Widget S.L., Spain, http://www.pocketwidget.com + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + + Authors: + * Javier Martinez Villacampa + +************************************************************************ */ +/** + * This class comes with all relevant information regarding + * the client's engine. + * + * This class is used by {@link qx.core.Environment} and should not be used + * directly. Please check its class comment for details how to use it. + * + * @internal + */ +qx.Bootstrap.define("qx.bom.client.Engine", { + // General: http://en.wikipedia.org/wiki/Browser_timeline + // Webkit: https://developer.apple.com/internet/safari/uamatrix.html + // Firefox: http://en.wikipedia.org/wiki/History_of_Mozilla_Firefox + // Maple: http://www.scribd.com/doc/46675822/2011-SDK2-0-Maple-Browser-Specification-V1-00 + statics : { + /** + * Returns the version of the engine. + * + * @return {String} The version number of the current engine. + * @internal + */ + getVersion : function(){ + + var agent = window.navigator.userAgent; + var version = ""; + if(qx.bom.client.Engine.__isOpera()){ + + // Opera has a special versioning scheme, where the second part is combined + // e.g. 8.54 which should be handled like 8.5.4 to be compatible to the + // common versioning system used by other browsers + if(/Opera[\s\/]([0-9]+)\.([0-9])([0-9]*)/.test(agent)){ + + // opera >= 10 has as a first verison 9.80 and adds the proper version + // in a separate "Version/" postfix + // http://my.opera.com/chooseopera/blog/2009/05/29/changes-in-operas-user-agent-string-format + if(agent.indexOf("Version/") != -1){ + + var match = agent.match(/Version\/(\d+)\.(\d+)/); + // ignore the first match, its the whole version string + version = match[1] + "." + match[2].charAt(0) + "." + match[2].substring(1, match[2].length); + } else { + + version = RegExp.$1 + "." + RegExp.$2; + if(RegExp.$3 != ""){ + + version += "." + RegExp.$3; + }; + }; + }; + } else if(qx.bom.client.Engine.__isWebkit()){ + + if(/AppleWebKit\/([^ ]+)/.test(agent)){ + + version = RegExp.$1; + // We need to filter these invalid characters + var invalidCharacter = RegExp("[^\\.0-9]").exec(version); + if(invalidCharacter){ + + version = version.slice(0, invalidCharacter.index); + }; + }; + } else if(qx.bom.client.Engine.__isGecko() || qx.bom.client.Engine.__isMaple()){ + + // Parse "rv" section in user agent string + if(/rv\:([^\);]+)(\)|;)/.test(agent)){ + + version = RegExp.$1; + }; + } else if(qx.bom.client.Engine.__isMshtml()){ + + var isTrident = /Trident\/([^\);]+)(\)|;)/.test(agent); + if(/MSIE\s+([^\);]+)(\)|;)/.test(agent)){ + + version = RegExp.$1; + // If the IE8 or IE9 is running in the compatibility mode, the MSIE value + // is set to an older version, but we need the correct version. The only + // way is to compare the trident version. + if(version < 8 && isTrident){ + + if(RegExp.$1 == "4.0"){ + + version = "8.0"; + } else if(RegExp.$1 == "5.0"){ + + version = "9.0"; + }; + }; + } else if(isTrident){ + + // IE 11 dropped the "MSIE" string + var match = /\brv\:(\d+?\.\d+?)\b/.exec(agent); + if(match){ + + version = match[1]; + }; + }; + } else { + + var failFunction = window.qxFail; + if(failFunction && typeof failFunction === "function"){ + + version = failFunction().FULLVERSION; + } else { + + version = "1.9.0.0"; + qx.Bootstrap.warn("Unsupported client: " + agent + "! Assumed gecko version 1.9.0.0 (Firefox 3.0)."); + }; + };;; + return version; + }, + /** + * Returns the name of the engine. + * + * @return {String} The name of the current engine. + * @internal + */ + getName : function(){ + + var name; + if(qx.bom.client.Engine.__isOpera()){ + + name = "opera"; + } else if(qx.bom.client.Engine.__isWebkit()){ + + name = "webkit"; + } else if(qx.bom.client.Engine.__isGecko() || qx.bom.client.Engine.__isMaple()){ + + name = "gecko"; + } else if(qx.bom.client.Engine.__isMshtml()){ + + name = "mshtml"; + } else { + + // check for the fallback + var failFunction = window.qxFail; + if(failFunction && typeof failFunction === "function"){ + + name = failFunction().NAME; + } else { + + name = "gecko"; + qx.Bootstrap.warn("Unsupported client: " + window.navigator.userAgent + "! Assumed gecko version 1.9.0.0 (Firefox 3.0)."); + }; + };;; + return name; + }, + /** + * Internal helper for checking for opera (presto powered). + * + * Note that with opera >= 15 their engine switched to blink, so + * things like "window.opera" don't work anymore or changed (e.g. user agent). + * + * @return {Boolean} true, if its opera (presto powered). + */ + __isOpera : function(){ + + return window.opera && Object.prototype.toString.call(window.opera) == "[object Opera]"; + }, + /** + * Internal helper for checking for webkit. + * @return {Boolean} true, if its webkit. + */ + __isWebkit : function(){ + + return window.navigator.userAgent.indexOf("AppleWebKit/") != -1; + }, + /** + * Internal helper for checking for Maple . + * Maple is used in Samsung SMART TV 2010-2011 models. It's based on Gecko + * engine 1.8.1.11. + * @return {Boolean} true, if its maple. + */ + __isMaple : function(){ + + return window.navigator.userAgent.indexOf("Maple") != -1; + }, + /** + * Internal helper for checking for gecko. + * + * Note: + * "window.controllers" is gone/hidden with Firefox 30+ + * "window.navigator.mozApps" is supported since Firefox 11+ + * "window.navigator.product" is actually useless cause the HTML5 spec + * states it should be the constant "Gecko". + * + * - https://developer.mozilla.org/en-US/docs/Web/API/Window.controllers + * - https://developer.mozilla.org/en-US/docs/Web/API/Navigator.mozApps + * - http://www.w3.org/html/wg/drafts/html/master/webappapis.html#navigatorid + * + * @return {Boolean} true, if its gecko. + */ + __isGecko : function(){ + + return (window.controllers || window.navigator.mozApps) && window.navigator.product === "Gecko" && window.navigator.userAgent.indexOf("Maple") == -1 && window.navigator.userAgent.indexOf("Trident") == -1; + }, + /** + * Internal helper to check for MSHTML. + * @return {Boolean} true, if its MSHTML. + */ + __isMshtml : function(){ + + return window.navigator.cpuClass && (/MSIE\s+([^\);]+)(\)|;)/.test(window.navigator.userAgent) || /Trident\/\d+?\.\d+?/.test(window.navigator.userAgent)); + } + }, + defer : function(statics){ + + qx.core.Environment.add("engine.version", statics.getVersion); + qx.core.Environment.add("engine.name", statics.getName); + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2011 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Martin Wittemann (martinwittemann) + +************************************************************************ */ +/** + * The main purpose of this class to hold all checks about ECMAScript. + * + * This class is used by {@link qx.core.Environment} and should not be used + * directly. Please check its class comment for details how to use it. + * + * @internal + */ +qx.Bootstrap.define("qx.bom.client.EcmaScript", { + statics : { + /** + * Returns the name of the Error object property that holds stack trace + * information or null if the client does not provide any. + * + * @internal + * @return {String|null} stack, stacktrace or + * null + */ + getStackTrace : function(){ + + var propName; + var e = new Error("e"); + propName = e.stack ? "stack" : e.stacktrace ? "stacktrace" : null; + // only thrown errors have the stack property in IE10 and PhantomJS + if(!propName){ + + try{ + + throw e; + } catch(ex) { + + e = ex; + }; + }; + return e.stacktrace ? "stacktrace" : e.stack ? "stack" : null; + }, + /** + * Checks if 'indexOf' is supported on the Array object. + * @internal + * @return {Boolean} true, if the method is available. + */ + getArrayIndexOf : function(){ + + return !!Array.prototype.indexOf; + }, + /** + * Checks if 'lastIndexOf' is supported on the Array object. + * @internal + * @return {Boolean} true, if the method is available. + */ + getArrayLastIndexOf : function(){ + + return !!Array.prototype.lastIndexOf; + }, + /** + * Checks if 'forEach' is supported on the Array object. + * @internal + * @return {Boolean} true, if the method is available. + */ + getArrayForEach : function(){ + + return !!Array.prototype.forEach; + }, + /** + * Checks if 'filter' is supported on the Array object. + * @internal + * @return {Boolean} true, if the method is available. + */ + getArrayFilter : function(){ + + return !!Array.prototype.filter; + }, + /** + * Checks if 'map' is supported on the Array object. + * @internal + * @return {Boolean} true, if the method is available. + */ + getArrayMap : function(){ + + return !!Array.prototype.map; + }, + /** + * Checks if 'some' is supported on the Array object. + * @internal + * @return {Boolean} true, if the method is available. + */ + getArraySome : function(){ + + return !!Array.prototype.some; + }, + /** + * Checks if 'every' is supported on the Array object. + * @internal + * @return {Boolean} true, if the method is available. + */ + getArrayEvery : function(){ + + return !!Array.prototype.every; + }, + /** + * Checks if 'reduce' is supported on the Array object. + * @internal + * @return {Boolean} true, if the method is available. + */ + getArrayReduce : function(){ + + return !!Array.prototype.reduce; + }, + /** + * Checks if 'reduceRight' is supported on the Array object. + * @internal + * @return {Boolean} true, if the method is available. + */ + getArrayReduceRight : function(){ + + return !!Array.prototype.reduceRight; + }, + /** + * Checks if 'toString' is supported on the Error object and + * its working as expected. + * @internal + * @return {Boolean} true, if the method is available. + */ + getErrorToString : function(){ + + return typeof Error.prototype.toString == "function" && Error.prototype.toString() !== "[object Error]"; + }, + /** + * Checks if 'bind' is supported on the Function object. + * @internal + * @return {Boolean} true, if the method is available. + */ + getFunctionBind : function(){ + + return typeof Function.prototype.bind === "function"; + }, + /** + * Checks if 'keys' is supported on the Object object. + * @internal + * @return {Boolean} true, if the method is available. + */ + getObjectKeys : function(){ + + return !!Object.keys; + }, + /** + * Checks if 'now' is supported on the Date object. + * @internal + * @return {Boolean} true, if the method is available. + */ + getDateNow : function(){ + + return !!Date.now; + }, + /** + * Checks if 'trim' is supported on the String object. + * @internal + * @return {Boolean} true, if the method is available. + */ + getStringTrim : function(){ + + return typeof String.prototype.trim === "function"; + } + }, + defer : function(statics){ + + // array polyfill + qx.core.Environment.add("ecmascript.array.indexof", statics.getArrayIndexOf); + qx.core.Environment.add("ecmascript.array.lastindexof", statics.getArrayLastIndexOf); + qx.core.Environment.add("ecmascript.array.foreach", statics.getArrayForEach); + qx.core.Environment.add("ecmascript.array.filter", statics.getArrayFilter); + qx.core.Environment.add("ecmascript.array.map", statics.getArrayMap); + qx.core.Environment.add("ecmascript.array.some", statics.getArraySome); + qx.core.Environment.add("ecmascript.array.every", statics.getArrayEvery); + qx.core.Environment.add("ecmascript.array.reduce", statics.getArrayReduce); + qx.core.Environment.add("ecmascript.array.reduceright", statics.getArrayReduceRight); + // date polyfill + qx.core.Environment.add("ecmascript.date.now", statics.getDateNow); + // error bugfix + qx.core.Environment.add("ecmascript.error.toString", statics.getErrorToString); + qx.core.Environment.add("ecmascript.error.stacktrace", statics.getStackTrace); + // function polyfill + qx.core.Environment.add("ecmascript.function.bind", statics.getFunctionBind); + // object polyfill + qx.core.Environment.add("ecmascript.object.keys", statics.getObjectKeys); + // string polyfill + qx.core.Environment.add("ecmascript.string.trim", statics.getStringTrim); + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2011 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Martin Wittemann (wittemann) + +************************************************************************ */ +/** + * This class is responsible for the normalization of the native 'Array' object. + * It checks if these methods are available and, if not, appends them to + * ensure compatibility in all browsers. + * For usage samples, check out the attached links. + * + * MDN documentation © Mozilla Contributors. + * + * @group (Polyfill) + */ +qx.Bootstrap.define("qx.lang.normalize.Array", { + statics : { + /** + * The indexOf() method returns the first index at which a given + * element can be found in the array, or -1 if it is not present. + * + * MDN documentation | + * Annotated ES5 Spec + * + * @param searchElement {var} Element to locate in the array. + * @param fromIndex {Integer?} The index to start the search at. + * If the index is greater than or equal to the array's length, + * -1 is returned, which means the array will not be searched. + * If the provided index value is a negative number, it is taken + * as the offset from the end of the array. Note: if the provided + * index is negative, the array is still searched from front to + * back. If the calculated index is less than 0, then the whole + * array will be searched. Default: 0 (Entire array is searched) + * @return {Integer} The first index at which the element was found or -1 + * if the element was not found in the array + */ + indexOf : function(searchElement, fromIndex){ + + if(fromIndex == null){ + + fromIndex = 0; + } else if(fromIndex < 0){ + + fromIndex = Math.max(0, this.length + fromIndex); + }; + for(var i = fromIndex;i < this.length;i++){ + + if(this[i] === searchElement){ + + return i; + }; + }; + return -1; + }, + /** + * The lastIndexOf() method returns the last index + * at which a given element can be found in the array, or -1 if + * it is not present. The array is searched backwards, starting at + * fromIndex. + * + * MDN documentation | + * Annotated ES5 Spec + * + * @param searchElement {var} Element to locate in the array. + * @param fromIndex {Integer?} The index at which to start + * searching backwards. Defaults to the array's length, i.e. the + * whole array will be searched. If the index is greater than or + * equal to the length of the array, the whole array will be + * searched. If negative, it is taken as the offset from the end + * of the array. Note that even when the index is negative, the + * array is still searched from back to front. If the calculated + * index is less than 0, -1 is returned, i.e. the array will not + * be searched. + * @return {Integer} The last index at which the element was found or -1 + * if the element was not found in the array + */ + lastIndexOf : function(searchElement, fromIndex){ + + if(fromIndex == null){ + + fromIndex = this.length - 1; + } else if(fromIndex < 0){ + + fromIndex = Math.max(0, this.length + fromIndex); + }; + for(var i = fromIndex;i >= 0;i--){ + + if(this[i] === searchElement){ + + return i; + }; + }; + return -1; + }, + /** + * The forEach() method executes a provided function + * once per array element. You can not break the loop with this function. + * If you want to do so, use {@link #some} or {@link #every}. + * + * MDN documentation | + * Annotated ES5 Spec + * + * @param callback {Function} Function to execute for each element. + * @param obj {Object?} Value to use as this when executing callback. + */ + forEach : function(callback, obj){ + + var l = this.length; + for(var i = 0;i < l;i++){ + + var value = this[i]; + if(value !== undefined){ + + callback.call(obj || window, value, i, this); + }; + }; + }, + /** + * The filter() method creates a new array with + * all elements that pass the test implemented by the provided + * function. + * + * MDN documentation | + * Annotated ES5 Spec + * + * @param callback {Function} Function to test each element of the array. + * @param obj {Object?} Value to use as this when executing callback. + * @return {Array} filtered array + */ + filter : function(callback, obj){ + + var res = []; + var l = this.length; + for(var i = 0;i < l;i++){ + + var value = this[i]; + if(value !== undefined){ + + if(callback.call(obj || window, value, i, this)){ + + res.push(this[i]); + }; + }; + }; + return res; + }, + /** + * The map() method creates a new array with + * the results of calling a provided function on every + * element in this array. + * + * MDN documentation | + * Annotated ES5 Spec + * + * @param callback {Function} Function that produces an element of the new Array, + * taking three arguments: + * + * @param obj {Object?} Value to use as this when executing callback. + * @return {Array} result array + */ + map : function(callback, obj){ + + var res = []; + var l = this.length; + for(var i = 0;i < l;i++){ + + var value = this[i]; + if(value !== undefined){ + + res[i] = callback.call(obj || window, value, i, this); + }; + }; + return res; + }, + /** + * The some() method tests whether some + * element in the array passes the test implemented by + * the provided function. + * + * MDN documentation | + * Annotated ES5 Spec + * + * @param callback {Function} Function to test for each element. + * @param obj {Object?} Value to use as this when executing callback. + * @return {Array} result array + */ + some : function(callback, obj){ + + var l = this.length; + for(var i = 0;i < l;i++){ + + var value = this[i]; + if(value !== undefined){ + + if(callback.call(obj || window, value, i, this)){ + + return true; + }; + }; + }; + return false; + }, + /** + * The every() method tests whether all elements + * in the array pass the test implemented by the provided function. + * + * MDN documentation | + * Annotated ES5 Spec + * + * @param callback {Function} Function to test for each element. + * @param obj {Object?} Value to use as this when executing callback. + * @return {Array} result array + */ + every : function(callback, obj){ + + var l = this.length; + for(var i = 0;i < l;i++){ + + var value = this[i]; + if(value !== undefined){ + + if(!callback.call(obj || window, value, i, this)){ + + return false; + }; + }; + }; + return true; + }, + /** + * The reduce() method applies a function against + * an accumulator and each value of the array (from left-to-right) + * has to reduce it to a single value. + * + * MDN documentation | + * Annotated ES5 Spec + * + * @param callback {Function} Function to execute on each value in + * the array, taking four arguments: + * + * @param init {Object?} Object to use as the first argument to the first call of the callback. + * @return {var} result value + */ + reduce : function(callback, init){ + + if(typeof callback !== "function"){ + + throw new TypeError("First argument is not callable"); + }; + if(init === undefined && this.length === 0){ + + throw new TypeError("Length is 0 and no second argument given"); + }; + var ret = init === undefined ? this[0] : init; + for(var i = init === undefined ? 1 : 0;i < this.length;i++){ + + if(i in this){ + + ret = callback.call(undefined, ret, this[i], i, this); + }; + }; + return ret; + }, + /** + * The reduceRight() method applies a function against an + * accumulator and each value of the array (from right-to-left) + * as to reduce it to a single value. + * + * MDN documentation | + * Annotated ES5 Spec + * + * @param callback {Function} Function to execute on each value in + * the array, taking four arguments: + * + * @param init {Object?} Object to use as the first argument to the first call of the callback. + * @return {var} return value + */ + reduceRight : function(callback, init){ + + if(typeof callback !== "function"){ + + throw new TypeError("First argument is not callable"); + }; + if(init === undefined && this.length === 0){ + + throw new TypeError("Length is 0 and no second argument given"); + }; + var ret = init === undefined ? this[this.length - 1] : init; + for(var i = init === undefined ? this.length - 2 : this.length - 1;i >= 0;i--){ + + if(i in this){ + + ret = callback.call(undefined, ret, this[i], i, this); + }; + }; + return ret; + } + }, + defer : function(statics){ + + if(!qx.core.Environment.get("ecmascript.array.indexof")){ + + Array.prototype.indexOf = statics.indexOf; + }; + if(!qx.core.Environment.get("ecmascript.array.lastindexof")){ + + Array.prototype.lastIndexOf = statics.lastIndexOf; + }; + if(!qx.core.Environment.get("ecmascript.array.foreach")){ + + Array.prototype.forEach = statics.forEach; + }; + if(!qx.core.Environment.get("ecmascript.array.filter")){ + + Array.prototype.filter = statics.filter; + }; + if(!qx.core.Environment.get("ecmascript.array.map")){ + + Array.prototype.map = statics.map; + }; + if(!qx.core.Environment.get("ecmascript.array.some")){ + + Array.prototype.some = statics.some; + }; + if(!qx.core.Environment.get("ecmascript.array.every")){ + + Array.prototype.every = statics.every; + }; + if(!qx.core.Environment.get("ecmascript.array.reduce")){ + + Array.prototype.reduce = statics.reduce; + }; + if(!qx.core.Environment.get("ecmascript.array.reduceright")){ + + Array.prototype.reduceRight = statics.reduceRight; + }; + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2007-2009 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Sebastian Werner (wpbasti) + * Fabian Jakobs (fjakobs) + + ====================================================================== + + This class uses ideas and code snipplets presented at + http://webreflection.blogspot.com/2008/05/habemus-array-unlocked-length-in-ie8.html + http://webreflection.blogspot.com/2008/05/stack-and-arrayobject-how-to-create.html + + Author: + Andrea Giammarchi + + License: + MIT: http://www.opensource.org/licenses/mit-license.php + + ====================================================================== + + This class uses documentation of the native Array methods from the MDC + documentation of Mozilla. + + License: + CC Attribution-Sharealike License: + http://creativecommons.org/licenses/by-sa/2.5/ + +************************************************************************ */ +/** + * This class is the common superclass for most array classes in + * qooxdoo. It supports all of the shiny 1.6 JavaScript array features + * like forEach and map. + * + * This class may be instantiated instead of the native Array if + * one wants to work with a feature-unified Array instead of the native + * one. This class uses native features whereever possible but fills + * all missing implementations with custom ones. + * + * Through the ability to extend from this class one could add even + * more utility features on top of it. + * + * @require(qx.bom.client.Engine) + * @require(qx.lang.normalize.Array) + */ +qx.Bootstrap.define("qx.type.BaseArray", { + extend : Array, + /* + ***************************************************************************** + CONSTRUCTOR + ***************************************************************************** + */ + /** + * Creates a new Array with the given length or the listed elements. + * + *
+   * var arr1 = new qx.type.BaseArray(arrayLength);
+   * var arr2 = new qx.type.BaseArray(item0, item1, ..., itemN);
+   * 
+ * + * * arrayLength: The initial length of the array. You can access + * this value using the length property. If the value specified is not a + * number, an array of length 1 is created, with the first element having + * the specified value. The maximum length allowed for an + * array is 2^32-1, i.e. 4,294,967,295. + * * itemN: A value for the element in that position in the + * array. When this form is used, the array is initialized with the specified + * values as its elements, and the array's length property is set to the + * number of arguments. + * + * @param length_or_items {Integer|var?null} The initial length of the array + * OR an argument list of values. + */ + construct : function(length_or_items){ + }, + /* + ***************************************************************************** + MEMBERS + ***************************************************************************** + */ + members : { + /** + * Converts a base array to a native Array + * + * @signature function() + * @return {Array} The native array + */ + toArray : null, + /** + * Returns the current number of items stored in the Array + * + * @signature function() + * @return {Integer} number of items + */ + valueOf : null, + /** + * Removes the last element from an array and returns that element. + * + * This method modifies the array. + * + * @signature function() + * @return {var} The last element of the array. + */ + pop : null, + /** + * Adds one or more elements to the end of an array and returns the new length of the array. + * + * This method modifies the array. + * + * @signature function(varargs) + * @param varargs {var} The elements to add to the end of the array. + * @return {Integer} The new array's length + */ + push : null, + /** + * Reverses the order of the elements of an array -- the first becomes the last, and the last becomes the first. + * + * This method modifies the array. + * + * @signature function() + * @return {Array} Returns the modified array (works in place) + */ + reverse : null, + /** + * Removes the first element from an array and returns that element. + * + * This method modifies the array. + * + * @signature function() + * @return {var} The first element of the array. + */ + shift : null, + /** + * Sorts the elements of an array. + * + * This method modifies the array. + * + * @signature function(compareFunction) + * @param compareFunction {Function?null} Specifies a function that defines the sort order. If omitted, + * the array is sorted lexicographically (in dictionary order) according to the string conversion of each element. + * @return {Array} Returns the modified array (works in place) + */ + sort : null, + /** + * Adds and/or removes elements from an array. + * + * @signature function(index, howMany, varargs) + * @param index {Integer} Index at which to start changing the array. If negative, will begin + * that many elements from the end. + * @param howMany {Integer} An integer indicating the number of old array elements to remove. + * If howMany is 0, no elements are removed. In this case, you should specify + * at least one new element. + * @param varargs {var?null} The elements to add to the array. If you don't specify any elements, + * splice simply removes elements from the array. + * @return {BaseArray} New array with the removed elements. + */ + splice : null, + /** + * Adds one or more elements to the front of an array and returns the new length of the array. + * + * This method modifies the array. + * + * @signature function(varargs) + * @param varargs {var} The elements to add to the front of the array. + * @return {Integer} The new array's length + */ + unshift : null, + /** + * Returns a new array comprised of this array joined with other array(s) and/or value(s). + * + * This method does not modify the array and returns a modified copy of the original. + * + * @signature function(varargs) + * @param varargs {Array|var} Arrays and/or values to concatenate to the resulting array. + * @return {qx.type.BaseArray} New array built of the given arrays or values. + */ + concat : null, + /** + * Joins all elements of an array into a string. + * + * @signature function(separator) + * @param separator {String} Specifies a string to separate each element of the array. The separator is + * converted to a string if necessary. If omitted, the array elements are separated with a comma. + * @return {String} The stringified values of all elements divided by the given separator. + */ + join : null, + /** + * Extracts a section of an array and returns a new array. + * + * @signature function(begin, end) + * @param begin {Integer} Zero-based index at which to begin extraction. As a negative index, start indicates + * an offset from the end of the sequence. slice(-2) extracts the second-to-last element and the last element + * in the sequence. + * @param end {Integer?length} Zero-based index at which to end extraction. slice extracts up to but not including end. + * slice(1,4) extracts the second element through the fourth element (elements indexed 1, 2, and 3). + * As a negative index, end indicates an offset from the end of the sequence. slice(2,-1) extracts the third element through the second-to-last element in the sequence. + * If end is omitted, slice extracts to the end of the sequence. + * @return {BaseArray} An new array which contains a copy of the given region. + */ + slice : null, + /** + * Returns a string representing the array and its elements. Overrides the Object.prototype.toString method. + * + * @signature function() + * @return {String} The string representation of the array. + */ + toString : null, + /** + * Returns the first (least) index of an element within the array equal to the specified value, or -1 if none is found. + * + * @signature function(searchElement, fromIndex) + * @param searchElement {var} Element to locate in the array. + * @param fromIndex {Integer?0} The index at which to begin the search. Defaults to 0, i.e. the + * whole array will be searched. If the index is greater than or equal to the length of the + * array, -1 is returned, i.e. the array will not be searched. If negative, it is taken as + * the offset from the end of the array. Note that even when the index is negative, the array + * is still searched from front to back. If the calculated index is less than 0, the whole + * array will be searched. + * @return {Integer} The index of the given element + */ + indexOf : null, + /** + * Returns the last (greatest) index of an element within the array equal to the specified value, or -1 if none is found. + * + * @signature function(searchElement, fromIndex) + * @param searchElement {var} Element to locate in the array. + * @param fromIndex {Integer?length} The index at which to start searching backwards. Defaults to + * the array's length, i.e. the whole array will be searched. If the index is greater than + * or equal to the length of the array, the whole array will be searched. If negative, it + * is taken as the offset from the end of the array. Note that even when the index is + * negative, the array is still searched from back to front. If the calculated index is + * less than 0, -1 is returned, i.e. the array will not be searched. + * @return {Integer} The index of the given element + */ + lastIndexOf : null, + /** + * Executes a provided function once per array element. + * + * forEach executes the provided function (callback) once for each + * element present in the array. callback is invoked only for indexes of the array + * which have assigned values; it is not invoked for indexes which have been deleted or which + * have never been assigned values. + * + * callback is invoked with three arguments: the value of the element, the index + * of the element, and the Array object being traversed. + * + * If a obj parameter is provided to forEach, it will be used + * as the this for each invocation of the callback. If it is not + * provided, or is null, the global object associated with callback + * is used instead. + * + * forEach does not mutate the array on which it is called. + * + * The range of elements processed by forEach is set before the first invocation of + * callback. Elements which are appended to the array after the call to + * forEach begins will not be visited by callback. If existing elements + * of the array are changed, or deleted, their value as passed to callback will be + * the value at the time forEach visits them; elements that are deleted are not visited. + * + * @signature function(callback, obj) + * @param callback {Function} Function to execute for each element. + * @param obj {Object} Object to use as this when executing callback. + */ + forEach : null, + /** + * Creates a new array with all elements that pass the test implemented by the provided + * function. + * + * filter calls a provided callback function once for each + * element in an array, and constructs a new array of all the values for which + * callback returns a true value. callback is invoked only + * for indexes of the array which have assigned values; it is not invoked for indexes + * which have been deleted or which have never been assigned values. Array elements which + * do not pass the callback test are simply skipped, and are not included + * in the new array. + * + * callback is invoked with three arguments: the value of the element, the + * index of the element, and the Array object being traversed. + * + * If a obj parameter is provided to filter, it will + * be used as the this for each invocation of the callback. + * If it is not provided, or is null, the global object associated with + * callback is used instead. + * + * filter does not mutate the array on which it is called. The range of + * elements processed by filter is set before the first invocation of + * callback. Elements which are appended to the array after the call to + * filter begins will not be visited by callback. If existing + * elements of the array are changed, or deleted, their value as passed to callback + * will be the value at the time filter visits them; elements that are deleted + * are not visited. + * + * @signature function(callback, obj) + * @param callback {Function} Function to test each element of the array. + * @param obj {Object} Object to use as this when executing callback. + * @return {BaseArray} The newly created array with all matching elements + */ + filter : null, + /** + * Creates a new array with the results of calling a provided function on every element in this array. + * + * map calls a provided callback function once for each element in an array, + * in order, and constructs a new array from the results. callback is invoked only for + * indexes of the array which have assigned values; it is not invoked for indexes which have been + * deleted or which have never been assigned values. + * + * callback is invoked with three arguments: the value of the element, the index of the + * element, and the Array object being traversed. + * + * If a obj parameter is provided to map, it will be used as the + * this for each invocation of the callback. If it is not provided, or is + * null, the global object associated with callback is used instead. + * + * map does not mutate the array on which it is called. + * + * The range of elements processed by map is set before the first invocation of + * callback. Elements which are appended to the array after the call to map + * begins will not be visited by callback. If existing elements of the array are changed, + * or deleted, their value as passed to callback will be the value at the time + * map visits them; elements that are deleted are not visited. + * + * @signature function(callback, obj) + * @param callback {Function} Function produce an element of the new Array from an element of the current one. + * @param obj {Object} Object to use as this when executing callback. + * @return {BaseArray} A new array which contains the return values of every item executed through the given function + */ + map : null, + /** + * Tests whether some element in the array passes the test implemented by the provided function. + * + * some executes the callback function once for each element present in + * the array until it finds one where callback returns a true value. If such an element + * is found, some immediately returns true. Otherwise, some + * returns false. callback is invoked only for indexes of the array which + * have assigned values; it is not invoked for indexes which have been deleted or which have never + * been assigned values. + * + * callback is invoked with three arguments: the value of the element, the index of the + * element, and the Array object being traversed. + * + * If a obj parameter is provided to some, it will be used as the + * this for each invocation of the callback. If it is not provided, or is + * null, the global object associated with callback is used instead. + * + * some does not mutate the array on which it is called. + * + * The range of elements processed by some is set before the first invocation of + * callback. Elements that are appended to the array after the call to some + * begins will not be visited by callback. If an existing, unvisited element of the array + * is changed by callback, its value passed to the visiting callback will + * be the value at the time that some visits that element's index; elements that are + * deleted are not visited. + * + * @signature function(callback, obj) + * @param callback {Function} Function to test for each element. + * @param obj {Object} Object to use as this when executing callback. + * @return {Boolean} Whether at least one elements passed the test + */ + some : null, + /** + * Tests whether all elements in the array pass the test implemented by the provided function. + * + * every executes the provided callback function once for each element + * present in the array until it finds one where callback returns a false value. If + * such an element is found, the every method immediately returns false. + * Otherwise, if callback returned a true value for all elements, every + * will return true. callback is invoked only for indexes of the array + * which have assigned values; it is not invoked for indexes which have been deleted or which have + * never been assigned values. + * + * callback is invoked with three arguments: the value of the element, the index of + * the element, and the Array object being traversed. + * + * If a obj parameter is provided to every, it will be used as + * the this for each invocation of the callback. If it is not provided, + * or is null, the global object associated with callback is used instead. + * + * every does not mutate the array on which it is called. The range of elements processed + * by every is set before the first invocation of callback. Elements which + * are appended to the array after the call to every begins will not be visited by + * callback. If existing elements of the array are changed, their value as passed + * to callback will be the value at the time every visits them; elements + * that are deleted are not visited. + * + * @signature function(callback, obj) + * @param callback {Function} Function to test for each element. + * @param obj {Object} Object to use as this when executing callback. + * @return {Boolean} Whether all elements passed the test + */ + every : null + } +}); +(function(){ + + function createStackConstructor(stack){ + + // In IE don't inherit from Array but use an empty object as prototype + // and copy the methods from Array + if((qx.core.Environment.get("engine.name") == "mshtml")){ + + Stack.prototype = { + length : 0, + $$isArray : true + }; + var args = "pop.push.reverse.shift.sort.splice.unshift.join.slice".split("."); + for(var length = args.length;length;){ + + Stack.prototype[args[--length]] = Array.prototype[args[length]]; + }; + }; + // Remember Array's slice method + var slice = Array.prototype.slice; + // Fix "concat" method + Stack.prototype.concat = function(){ + + var constructor = this.slice(0); + for(var i = 0,length = arguments.length;i < length;i++){ + + var copy; + if(arguments[i] instanceof Stack){ + + copy = slice.call(arguments[i], 0); + } else if(arguments[i] instanceof Array){ + + copy = arguments[i]; + } else { + + copy = [arguments[i]]; + }; + constructor.push.apply(constructor, copy); + }; + return constructor; + }; + // Fix "toString" method + Stack.prototype.toString = function(){ + + return slice.call(this, 0).toString(); + }; + // Fix "toLocaleString" + Stack.prototype.toLocaleString = function(){ + + return slice.call(this, 0).toLocaleString(); + }; + // Fix constructor + Stack.prototype.constructor = Stack; + // Add JS 1.6 Array features + Stack.prototype.indexOf = Array.prototype.indexOf; + Stack.prototype.lastIndexOf = Array.prototype.lastIndexOf; + Stack.prototype.forEach = Array.prototype.forEach; + Stack.prototype.some = Array.prototype.some; + Stack.prototype.every = Array.prototype.every; + var filter = Array.prototype.filter; + var map = Array.prototype.map; + // Fix methods which generates a new instance + // to return an instance of the same class + Stack.prototype.filter = function(){ + + var ret = new this.constructor; + ret.push.apply(ret, filter.apply(this, arguments)); + return ret; + }; + Stack.prototype.map = function(){ + + var ret = new this.constructor; + ret.push.apply(ret, map.apply(this, arguments)); + return ret; + }; + Stack.prototype.slice = function(){ + + var ret = new this.constructor; + ret.push.apply(ret, Array.prototype.slice.apply(this, arguments)); + return ret; + }; + Stack.prototype.splice = function(){ + + var ret = new this.constructor; + ret.push.apply(ret, Array.prototype.splice.apply(this, arguments)); + return ret; + }; + // Add new "toArray" method for convert a base array to a native Array + Stack.prototype.toArray = function(){ + + return Array.prototype.slice.call(this, 0); + }; + // Add valueOf() to return the length + Stack.prototype.valueOf = function(){ + + return this.length; + }; + // Return final class + return Stack; + }; + function Stack(length){ + + if(arguments.length === 1 && typeof length === "number"){ + + this.length = -1 < length && length === length >> .5 ? length : this.push(length); + } else if(arguments.length){ + + this.push.apply(this, arguments); + }; + }; + function PseudoArray(){ + }; + PseudoArray.prototype = []; + Stack.prototype = new PseudoArray; + Stack.prototype.length = 0; + qx.type.BaseArray = createStackConstructor(Stack); +})(); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2012 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Martin Wittemann (wittemann) + +************************************************************************ */ +/** + * The Core module's responsibility is to query the DOM for elements and offer + * these elements as a collection. The Core module itself does not offer any methods to + * work with the collection. These methods are added by the other included modules, + * such as Manipulating or Attributes. + * + * Core also provides the plugin API which allows modules to attach either + * static functions to the global q object or define methods on the + * collection it returns. + * + * By default, the core module is assigned to a global module named q. + * In case q is already defined, the name qxWeb + * is used instead. + * + * For further details, take a look at the documentation in the + * user manual. + * + * @ignore(q) + * + * @group (Core) + */ +qx.Bootstrap.define("qxWeb", { + extend : qx.type.BaseArray, + statics : { + // internal storage for all initializers + __init : [], + // internal reference to the used qx namespace + $$qx : qx, + /** + * Internal helper to initialize collections. + * + * @param arg {var} An array of Elements which will + * be initialized as {@link q}. All items in the array which are not + * either a window object, a DOM element node or a DOM document node will + * be ignored. + * @param clazz {Class} The class of the new collection. + * @return {q} A new initialized collection. + */ + $init : function(arg, clazz){ + + var clean = []; + for(var i = 0;i < arg.length;i++){ + + // check for node or window object + var isNode = !!(arg[i] && (arg[i].nodeType === 1 || arg[i].nodeType === 9 || arg[i].nodeType === 11)); + if(isNode){ + + clean.push(arg[i]); + continue; + }; + var isWindow = !!(arg[i] && arg[i].history && arg[i].location && arg[i].document); + if(isWindow){ + + clean.push(arg[i]); + }; + }; + if(arg[0] && arg[0].getAttribute && arg[0].getAttribute("data-qx-class")){ + + clazz = qx.Bootstrap.getByName(arg[0].getAttribute("data-qx-class")) || clazz; + }; + var col = qx.lang.Array.cast(clean, clazz); + for(var i = 0;i < qxWeb.__init.length;i++){ + + qxWeb.__init[i].call(col); + }; + return col; + }, + /** + * This is an API for module development and can be used to attach new methods + * to {@link q}. + * + * @param module {Map} A map containing the methods to attach. + */ + $attach : function(module){ + + for(var name in module){ + + { + }; + qxWeb.prototype[name] = module[name]; + }; + }, + /** + * This is an API for module development and can be used to attach new methods + * to {@link q}. + * + * @param module {Map} A map containing the methods to attach. + */ + $attachStatic : function(module){ + + for(var name in module){ + + { + }; + qxWeb[name] = module[name]; + }; + }, + /** + * This is an API for module development and can be used to attach new initialization + * methods to {@link q} which will be called when a new collection is + * created. + * + * @param init {Function} The initialization method for a module. + */ + $attachInit : function(init){ + + this.__init.push(init); + }, + /** + * Define a new class using the qooxdoo class system. + * + * @param name {String?} Name of the class. If null, the class will not be + * attached to a namespace. + * @param config {Map ? null} Class definition structure. The configuration map has the following keys: + * + * + * + * + * + * + * + * + * + *
NameTypeDescription
extendClassThe super class the current class inherits from.
constructFunctionThe constructor of the class.
staticsMapMap of static values / functions of the class.
membersMapMap of instance members of the class.
deferFunctionFunction that is called at the end of + * processing the class declaration.
+ * @return {Function} The defined class. + */ + define : function(name, config){ + + if(config == undefined){ + + config = name; + name = null; + }; + return qx.Bootstrap.define.call(qx.Bootstrap, name, config); + } + }, + /** + * Primary usage: + * Accepts a selector string and returns a collection of found items. The optional context + * element can be used to reduce the amount of found elements to children of the + * context element. If the context object is a collection, its first item is used. + * + * Secondary usage: + * Creates a collection from an existing DOM element, document node or window object + * (or an Array containing any such objects) + * + * Sizzle is used as selector engine. + * Check out the documentation + * for more details. + * + * @param selector {String|Element|Document|Window|Array} Valid selector (CSS3 + extensions), + * window object, DOM element/document or Array of DOM Elements. + * @param context {Element|q} Only the children of this element are considered. + * @return {q} A collection of DOM elements. + */ + construct : function(selector, context){ + + if(!selector && this instanceof qxWeb){ + + return this; + }; + if(!selector){ + + selector = []; + } else if(qx.Bootstrap.isString(selector)){ + + if(context instanceof qxWeb){ + + context = context[0]; + }; + selector = qx.bom.Selector.query(selector, context); + } else if((selector.nodeType === 1 || selector.nodeType === 9 || selector.nodeType === 11) || (selector.history && selector.location && selector.document)){ + + selector = [selector]; + };; + return qxWeb.$init(selector, qxWeb); + }, + members : { + /** + * Gets a new collection containing only those elements that passed the + * given filter. This can be either a selector expression or a filter + * function. + * + * @param selector {String|Function} Selector expression or filter function + * @return {q} New collection containing the elements that passed the filter + */ + filter : function(selector){ + + if(qx.lang.Type.isFunction(selector)){ + + return qxWeb.$init(Array.prototype.filter.call(this, selector), this.constructor); + }; + return qxWeb.$init(qx.bom.Selector.matches(selector, this), this.constructor); + }, + /** + * Recreates a collection which is free of all duplicate elements from the original. + * + * @return {q} Returns a copy with no duplicates + */ + unique : function(){ + + var unique = qx.lang.Array.unique(this); + return qxWeb.$init(unique, this.constructor); + }, + /** + * Returns a copy of the collection within the given range. + * + * @param begin {Number} The index to begin. + * @param end {Number?} The index to end. + * @return {q} A new collection containing a slice of the original collection. + */ + slice : function(begin, end){ + + // Old IEs return an empty array if the second argument is undefined + // check 'end' explicit for "undefined" [BUG #7322] + if(end !== undefined){ + + return qxWeb.$init(Array.prototype.slice.call(this, begin, end), this.constructor); + }; + return qxWeb.$init(Array.prototype.slice.call(this, begin), this.constructor); + }, + /** + * Removes the given number of items and returns the removed items as a new collection. + * This method can also add items. Take a look at the + * documentation of MDN for more details. + * + * @param index {Number} The index to begin. + * @param howMany {Number} the amount of items to remove. + * @param varargs {var} As many items as you want to add. + * @return {q} A new collection containing the removed items. + */ + splice : function(index, howMany, varargs){ + + return qxWeb.$init(Array.prototype.splice.apply(this, arguments), this.constructor); + }, + /** + * Returns a new collection containing the modified elements. For more details, check out the + * MDN documentation. + * + * @param callback {Function} Function which produces the new element. + * @param thisarg {var} Context of the callback. + * @return {q} New collection containing the elements that passed the filter + */ + map : function(callback, thisarg){ + + return qxWeb.$init(Array.prototype.map.apply(this, arguments), qxWeb); + }, + /** + * Returns a copy of the collection including the given elements. + * + * @param varargs {var} As many items as you want to add. + * @return {q} A new collection containing all items. + */ + concat : function(varargs){ + + var clone = Array.prototype.slice.call(this, 0); + for(var i = 0;i < arguments.length;i++){ + + if(arguments[i] instanceof qxWeb){ + + clone = clone.concat(Array.prototype.slice.call(arguments[i], 0)); + } else { + + clone.push(arguments[i]); + }; + }; + return qxWeb.$init(clone, this.constructor); + }, + /** + * Returns the index of the given element within the current + * collection or -1 if the element is not in the collection + * @param elem {Element|Element[]|qxWeb} Element or collection of elements + * @return {Number} The element's index + */ + indexOf : function(elem){ + + if(!elem){ + + return -1; + }; + if(qx.Bootstrap.isArray(elem)){ + + elem = elem[0]; + }; + for(var i = 0,l = this.length;i < l;i++){ + + if(this[i] === elem){ + + return i; + }; + }; + return -1; + }, + /** + * Calls the browser's native debugger to easily allow debugging within + * chained calls. + * + * @return {q} The collection for chaining + * @ignore(debugger) + */ + debug : function(){ + + debugger; + return this; + }, + /** + * Calls a function for each DOM element or document fragment in the + * collection. This is used for DOM manipulations which can't be + * applied to document nodes or window objects. + * + * @param func {Function} Callback function. Will be called with three arguments: + * The element, the element's index within the collection and the collection itself. + * @param ctx {Object} The context for the callback function (default: The collection) + * @return {q} The collection for chaining + */ + _forEachElement : function(func, ctx){ + + for(var i = 0,l = this.length;i < l;i++){ + + if(this[i] && (this[i].nodeType === 1 || this[i].nodeType === 11)){ + + func.apply(ctx || this, [this[i], i, this]); + }; + }; + return this; + }, + /** + * Calls a function for each DOM element node in the collection. Each node is wrapped + * in a collection before the function is called. + * + * @param func {Function} Callback function. Will be called with three arguments: + * The element wrappend in a collection, the element's index within the collection and + * the collection itself. + * @param ctx {Object} The context for the callback function (default: The collection) + * @return {q} The collection for chaining + */ + _forEachElementWrapped : function(func, ctx){ + + this._forEachElement(function(item, idx, arr){ + + func.apply(this, [qxWeb(item), idx, arr]); + }, ctx); + return this; + } + }, + /** + * @ignore(q) + */ + defer : function(statics){ + + if(window.q == undefined){ + + q = statics; + }; + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2012 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Martin Wittemann (wittemann) + +************************************************************************ */ +/** + * This class is responsible for the normalization of the native 'Date' object. + * It checks if these methods are available and, if not, appends them to + * ensure compatibility in all browsers. + * For usage samples, check out the attached links. + * + * @group (Polyfill) + */ +qx.Bootstrap.define("qx.lang.normalize.Date", { + statics : { + /** + * Returns the time elapsed since January 1, 1970 in milliseconds. + * + * MDN documentation | + * Annotated ES5 Spec + * + * @return {Integer} Milliseconds since the Unix Epoch + */ + now : function(){ + + return +new Date(); + } + }, + defer : function(statics){ + + // Date.now + if(!qx.core.Environment.get("ecmascript.date.now")){ + + Date.now = statics.now; + }; + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Sebastian Werner (wpbasti) + * Andreas Ecker (ecker) + + ====================================================================== + + This class contains code based on the following work: + + * jQuery + http://jquery.com + Version 1.3.1 + + Copyright: + 2009 John Resig + + License: + MIT: http://www.opensource.org/licenses/mit-license.php + +************************************************************************ */ +/** + * Static helper functions for arrays with a lot of often used convenience + * methods like remove or contains. + * + * The native JavaScript Array is not modified by this class. However, + * there are modifications to the native Array in {@link qx.lang.normalize.Array} for + * browsers that do not support certain JavaScript features natively . + * + * @ignore(qx.data) + * @ignore(qx.data.IListData) + * @ignore(qx.Class.*) + * @require(qx.lang.normalize.Date) + */ +qx.Bootstrap.define("qx.lang.Array", { + statics : { + /** + * Converts an array like object to any other array like + * object. + * + * Attention: The returned array may be same + * instance as the incoming one if the constructor is identical! + * + * @param object {var} any array-like object + * @param constructor {Function} constructor of the new instance + * @param offset {Integer?0} position to start from + * @return {Array} the converted array + */ + cast : function(object, constructor, offset){ + + if(object.constructor === constructor){ + + return object; + }; + if(qx.data && qx.data.IListData){ + + if(qx.Class && qx.Class.hasInterface(object, qx.data.IListData)){ + + var object = object.toArray(); + }; + }; + // Create from given constructor + var ret = new constructor; + // Some collections in mshtml are not able to be sliced. + // These lines are a special workaround for this client. + if((qx.core.Environment.get("engine.name") == "mshtml")){ + + if(object.item){ + + for(var i = offset || 0,l = object.length;i < l;i++){ + + ret.push(object[i]); + }; + return ret; + }; + }; + // Copy over items + if(Object.prototype.toString.call(object) === "[object Array]" && offset == null){ + + ret.push.apply(ret, object); + } else { + + ret.push.apply(ret, Array.prototype.slice.call(object, offset || 0)); + }; + return ret; + }, + /** + * Convert an arguments object into an array. + * + * @param args {arguments} arguments object + * @param offset {Integer?0} position to start from + * @return {Array} a newly created array (copy) with the content of the arguments object. + */ + fromArguments : function(args, offset){ + + return Array.prototype.slice.call(args, offset || 0); + }, + /** + * Convert a (node) collection into an array + * + * @param coll {var} node collection + * @return {Array} a newly created array (copy) with the content of the node collection. + */ + fromCollection : function(coll){ + + // The native Array.slice cannot be used with some Array-like objects + // including NodeLists in older IEs + if((qx.core.Environment.get("engine.name") == "mshtml")){ + + if(coll.item){ + + var arr = []; + for(var i = 0,l = coll.length;i < l;i++){ + + arr[i] = coll[i]; + }; + return arr; + }; + }; + return Array.prototype.slice.call(coll, 0); + }, + /** + * Expand shorthand definition to a four element list. + * This is an utility function for padding/margin and all other shorthand handling. + * + * @param input {Array} arr with one to four elements + * @return {Array} an arr with four elements + */ + fromShortHand : function(input){ + + var len = input.length; + var result = qx.lang.Array.clone(input); + // Copy Values (according to the length) + switch(len){case 1: + result[1] = result[2] = result[3] = result[0]; + break;case 2: + result[2] = result[0];// no break here + case 3: + result[3] = result[1];}; + // Return list with 4 items + return result; + }, + /** + * Return a copy of the given array + * + * @param arr {Array} the array to copy + * @return {Array} copy of the array + */ + clone : function(arr){ + + return arr.concat(); + }, + /** + * Insert an element at a given position into the array + * + * @param arr {Array} the array + * @param obj {var} the element to insert + * @param i {Integer} position where to insert the element into the array + * @return {Array} the array + */ + insertAt : function(arr, obj, i){ + + arr.splice(i, 0, obj); + return arr; + }, + /** + * Insert an element into the array before a given second element. + * + * @param arr {Array} the array + * @param obj {var} object to be inserted + * @param obj2 {var} insert obj1 before this object + * @return {Array} the array + */ + insertBefore : function(arr, obj, obj2){ + + var i = arr.indexOf(obj2); + if(i == -1){ + + arr.push(obj); + } else { + + arr.splice(i, 0, obj); + }; + return arr; + }, + /** + * Insert an element into the array after a given second element. + * + * @param arr {Array} the array + * @param obj {var} object to be inserted + * @param obj2 {var} insert obj1 after this object + * @return {Array} the array + */ + insertAfter : function(arr, obj, obj2){ + + var i = arr.indexOf(obj2); + if(i == -1 || i == (arr.length - 1)){ + + arr.push(obj); + } else { + + arr.splice(i + 1, 0, obj); + }; + return arr; + }, + /** + * Remove an element from the array at the given index + * + * @param arr {Array} the array + * @param i {Integer} index of the element to be removed + * @return {var} The removed element. + */ + removeAt : function(arr, i){ + + return arr.splice(i, 1)[0]; + }, + /** + * Remove all elements from the array + * + * @param arr {Array} the array + * @return {Array} empty array + */ + removeAll : function(arr){ + + arr.length = 0; + return this; + }, + /** + * Append the elements of an array to the array + * + * @param arr1 {Array} the array + * @param arr2 {Array} the elements of this array will be appended to other one + * @return {Array} The modified array. + * @throws {Error} if one of the arguments is not an array + */ + append : function(arr1, arr2){ + + { + }; + Array.prototype.push.apply(arr1, arr2); + return arr1; + }, + /** + * Modifies the first array as it removes all elements + * which are listed in the second array as well. + * + * @param arr1 {Array} the array + * @param arr2 {Array} the elements of this array will be excluded from the other one + * @return {Array} The modified array. + * @throws {Error} if one of the arguments is not an array + */ + exclude : function(arr1, arr2){ + + { + }; + for(var i = 0,il = arr2.length,index;i < il;i++){ + + index = arr1.indexOf(arr2[i]); + if(index != -1){ + + arr1.splice(index, 1); + }; + }; + return arr1; + }, + /** + * Remove an element from the array. + * + * @param arr {Array} the array + * @param obj {var} element to be removed from the array + * @return {var} the removed element + */ + remove : function(arr, obj){ + + var i = arr.indexOf(obj); + if(i != -1){ + + arr.splice(i, 1); + return obj; + }; + }, + /** + * Whether the array contains the given element + * + * @param arr {Array} the array + * @param obj {var} object to look for + * @return {Boolean} whether the arr contains the element + */ + contains : function(arr, obj){ + + return arr.indexOf(obj) !== -1; + }, + /** + * Check whether the two arrays have the same content. Checks only the + * equality of the arrays' content. + * + * @param arr1 {Array} first array + * @param arr2 {Array} second array + * @return {Boolean} Whether the two arrays are equal + */ + equals : function(arr1, arr2){ + + var length = arr1.length; + if(length !== arr2.length){ + + return false; + }; + for(var i = 0;i < length;i++){ + + if(arr1[i] !== arr2[i]){ + + return false; + }; + }; + return true; + }, + /** + * Returns the sum of all values in the given array. Supports + * numeric values only. + * + * @param arr {Number[]} Array to process + * @return {Number} The sum of all values. + */ + sum : function(arr){ + + var result = 0; + for(var i = 0,l = arr.length;i < l;i++){ + + if(arr[i] != undefined){ + + result += arr[i]; + }; + }; + return result; + }, + /** + * Returns the highest value in the given array. Supports + * numeric values only. + * + * @param arr {Number[]} Array to process + * @return {Number | null} The highest of all values or undefined if array is empty. + */ + max : function(arr){ + + { + }; + var i,len = arr.length,result = arr[0]; + for(i = 1;i < len;i++){ + + if(arr[i] > result){ + + result = arr[i]; + }; + }; + return result === undefined ? null : result; + }, + /** + * Returns the lowest value in the given array. Supports + * numeric values only. + * + * @param arr {Number[]} Array to process + * @return {Number | null} The lowest of all values or undefined if array is empty. + */ + min : function(arr){ + + { + }; + var i,len = arr.length,result = arr[0]; + for(i = 1;i < len;i++){ + + if(arr[i] < result){ + + result = arr[i]; + }; + }; + return result === undefined ? null : result; + }, + /** + * Recreates an array which is free of all duplicate elements from the original. + * + * This method does not modify the original array! + * + * Keep in mind that this methods deletes undefined indexes. + * + * @param arr {Array} Incoming array + * @return {Array} Returns a copy with no duplicates + */ + unique : function(arr){ + + var ret = [],doneStrings = { + },doneNumbers = { + },doneObjects = { + }; + var value,count = 0; + var key = "qx" + Date.now(); + var hasNull = false,hasFalse = false,hasTrue = false; + // Rebuild array and omit duplicates + for(var i = 0,len = arr.length;i < len;i++){ + + value = arr[i]; + // Differ between null, primitives and reference types + if(value === null){ + + if(!hasNull){ + + hasNull = true; + ret.push(value); + }; + } else if(value === undefined){ + } else if(value === false){ + + if(!hasFalse){ + + hasFalse = true; + ret.push(value); + }; + } else if(value === true){ + + if(!hasTrue){ + + hasTrue = true; + ret.push(value); + }; + } else if(typeof value === "string"){ + + if(!doneStrings[value]){ + + doneStrings[value] = 1; + ret.push(value); + }; + } else if(typeof value === "number"){ + + if(!doneNumbers[value]){ + + doneNumbers[value] = 1; + ret.push(value); + }; + } else { + + var hash = value[key]; + if(hash == null){ + + hash = value[key] = count++; + }; + if(!doneObjects[hash]){ + + doneObjects[hash] = value; + ret.push(value); + }; + };;;;; + }; + // Clear object hashs + for(var hash in doneObjects){ + + try{ + + delete doneObjects[hash][key]; + } catch(ex) { + + try{ + + doneObjects[hash][key] = null; + } catch(ex1) { + + throw new Error("Cannot clean-up map entry doneObjects[" + hash + "][" + key + "]"); + }; + }; + }; + return ret; + } + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2008-2010 Sebastian Werner, http://sebastian-werner.net + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Sebastian Werner (wpbasti) + * Fabian Jakobs (fjakobs) + * Andreas Ecker (ecker) + + ====================================================================== + + This class contains code based on the following work: + + * Sizzle CSS Selector Engine - v1.10.18 + + Homepage: + http://sizzlejs.com/ + + Documentation: + https://github.com/jquery/sizzle/wiki/Sizzle-Documentation + + Discussion: + https://groups.google.com/forum/#!forum/sizzlejs + + Code: + https://github.com/jquery/sizzle + + Copyright: + (c) 2009, 2013 jQuery Foundation and other contributors + + License: + MIT: http://www.opensource.org/licenses/mit-license.php + + ---------------------------------------------------------------------- + + Copyright 2013 jQuery Foundation and other contributors + http://jquery.com/ + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + ---------------------------------------------------------------------- + + Version: + v1.10.18 + commit a9eb3ca3c5e1b568057390f73da385809ac69340 + +************************************************************************ */ +/** + * The selector engine supports virtually all CSS 3 Selectors – this even + * includes some parts that are infrequently implemented such as escaped + * selectors (.foo\\+bar), Unicode selectors, and results returned + * in document order. There are a few notable exceptions to the CSS 3 selector + * support: + * + * * :root + * * :target + * * :nth-last-child + * * :nth-of-type + * * :nth-last-of-type + * * :first-of-type + * * :last-of-type + * * :only-of-type + * * :lang() + * + * In addition to the CSS 3 Selectors the engine supports the following + * additional selectors or conventions. + * + * *Changes* + * + * * :not(a.b): Supports non-simple selectors in :not() (most browsers only support :not(a), for example). + * * :not(div > p): Supports full selectors in :not(). + * * :not(div, p): Supports multiple selectors in :not(). + * * [NAME=VALUE]: Doesn't require quotes around the specified value in an attribute selector. + * + * *Additions* + * + * * [NAME!=VALUE]: Finds all elements whose NAME attribute doesn't match the specified value. Is equivalent to doing :not([NAME=VALUE]). + * * :contains(TEXT): Finds all elements whose textual context contains the word TEXT (case sensitive). + * * :header: Finds all elements that are a header element (h1, h2, h3, h4, h5, h6). + * * :parent: Finds all elements that contains another element. + * + * *Positional Selector Additions* + * + * * :first/:last: Finds the first or last matching element on the page. (e.g. div:first would find the first div on the page, in document order) + * * :even/:odd: Finds every other element on the page (counting begins at 0, so :even would match the first element). + * * :eq/:nth: Finds the Nth element on the page (e.g. :eq(5) finds the 6th element on the page). + * * :lt/:gt: Finds all elements at positions less than or greater than the specified positions. + * + * *Form Selector Additions* + * + * * :input: Finds all input elements (includes textareas, selects, and buttons). + * * :text, :checkbox, :file, :password, :submit, :image, :reset, :button: Finds the input element with the specified input type (:button also finds button elements). + * + * Based on Sizzle by John Resig, see: + * + * * http://sizzlejs.com/ + * + * For further usage details also have a look at the wiki page at: + * + * * https://github.com/jquery/sizzle/wiki/Sizzle-Home + */ +qx.Bootstrap.define("qx.bom.Selector", { + statics : { + /** + * Queries the document for the given selector. Supports all CSS3 selectors + * plus some extensions as mentioned in the class description. + * + * @signature function(selector, context) + * @param selector {String} Valid selector (CSS3 + extensions) + * @param context {Element} Context element (result elements must be children of this element) + * @return {Array} Matching elements + */ + query : null, + /** + * Returns an reduced array which only contains the elements from the given + * array which matches the given selector + * + * @signature function(selector, set) + * @param selector {String} Selector to filter given set + * @param set {Array} List to filter according to given selector + * @return {Array} New array containing matching elements + */ + matches : null + } +}); +/** + * Below is the original Sizzle code. Snapshot date is mentioned in the head of + * this file. + * @lint ignoreUnused(j, rnot, rendsWithNot) + */ +/*! + * Sizzle CSS Selector Engine v1.10.18 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-02-05 + */ +(function(window){ + + var i,support,Expr,getText,isXML,compile,select,outermostContext,sortInput,hasDuplicate,// Local document vars + setDocument,document,docElem,documentIsHTML,rbuggyQSA,rbuggyMatches,matches,contains,// Instance-specific data + expando = "sizzle" + -(new Date()),preferredDoc = window.document,dirruns = 0,done = 0,classCache = createCache(),tokenCache = createCache(),compilerCache = createCache(),sortOrder = function(a, b){ + + if(a === b){ + + hasDuplicate = true; + }; + return 0; + },// General-purpose constants + strundefined = typeof undefined,MAX_NEGATIVE = 1 << 31,// Instance methods + hasOwn = ({ + }).hasOwnProperty,arr = [],pop = arr.pop,push_native = arr.push,push = arr.push,slice = arr.slice,// Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function(elem){ + + var i = 0,len = this.length; + for(;i < len;i++){ + + if(this[i] === elem){ + + return i; + }; + }; + return -1; + },booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",// Regular expressions + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]",// http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",// Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace("w", "w#"),// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",// Prefer arguments quoted, + // then not containing pseudos/brackets, + // then attribute selectors/non-parenthetical expressions, + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace(3, 8) + ")*)|.*)\\)|)",// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g"),rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*"),rcombinators = new RegExp("^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"),rattributeQuotes = new RegExp("=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g"),rpseudo = new RegExp(pseudos),ridentifier = new RegExp("^" + identifier + "$"),matchExpr = { + "ID" : new RegExp("^#(" + characterEncoding + ")"), + "CLASS" : new RegExp("^\\.(" + characterEncoding + ")"), + "TAG" : new RegExp("^(" + characterEncoding.replace("w", "w*") + ")"), + "ATTR" : new RegExp("^" + attributes), + "PSEUDO" : new RegExp("^" + pseudos), + "CHILD" : new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i"), + "bool" : new RegExp("^(?:" + booleans + ")$", "i"), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext" : new RegExp("^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i") + },rinputs = /^(?:input|select|textarea|button)$/i,rheader = /^h\d$/i,rnative = /^[^{]+\{\s*\[native \w/,// Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,rsibling = /[+~]/,rescape = /'|\\/g,// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp("\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig"),funescape = function(_, escaped, escapedWhitespace){ + + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? escaped : high < 0 ? // BMP codepoint + String.fromCharCode(high + 0x10000) : // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode(high >> 10 | 0xD800, high & 0x3FF | 0xDC00); + }; + // Optimize for push.apply( _, NodeList ) + try{ + + push.apply((arr = slice.call(preferredDoc.childNodes)), preferredDoc.childNodes); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[preferredDoc.childNodes.length].nodeType; + } catch(e) { + + push = { + apply : arr.length ? // Leverage slice if possible + function(target, els){ + + push_native.apply(target, slice.call(els)); + } : // Support: IE<9 + // Otherwise append directly + function(target, els){ + + var j = target.length,i = 0; + // Can't trust NodeList.length + while((target[j++] = els[i++])){ + }; + target.length = j - 1; + } + }; + }; + function Sizzle(selector, context, results, seed){ + + var match,elem,m,nodeType,// QSA vars + i,groups,old,nid,newContext,newSelector; + if((context ? context.ownerDocument || context : preferredDoc) !== document){ + + setDocument(context); + }; + context = context || document; + results = results || []; + if(!selector || typeof selector !== "string"){ + + return results; + }; + if((nodeType = context.nodeType) !== 1 && nodeType !== 9){ + + return []; + }; + if(documentIsHTML && !seed){ + + // Shortcuts + if((match = rquickExpr.exec(selector))){ + + // Speed-up: Sizzle("#ID") + if((m = match[1])){ + + if(nodeType === 9){ + + elem = context.getElementById(m); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document (jQuery #6963) + if(elem && elem.parentNode){ + + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if(elem.id === m){ + + results.push(elem); + return results; + }; + } else { + + return results; + }; + } else { + + // Context is not a document + if(context.ownerDocument && (elem = context.ownerDocument.getElementById(m)) && contains(context, elem) && elem.id === m){ + + results.push(elem); + return results; + }; + }; + } else if(match[2]){ + + push.apply(results, context.getElementsByTagName(selector)); + return results; + } else if((m = match[3]) && support.getElementsByClassName && context.getElementsByClassName){ + + push.apply(results, context.getElementsByClassName(m)); + return results; + };; + }; + // QSA path + if(support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))){ + + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if(nodeType === 1 && context.nodeName.toLowerCase() !== "object"){ + + groups = tokenize(selector); + if((old = context.getAttribute("id"))){ + + nid = old.replace(rescape, "\\$&"); + } else { + + context.setAttribute("id", nid); + }; + nid = "[id='" + nid + "'] "; + i = groups.length; + while(i--){ + + groups[i] = nid + toSelector(groups[i]); + }; + newContext = rsibling.test(selector) && testContext(context.parentNode) || context; + newSelector = groups.join(","); + }; + if(newSelector){ + + try{ + + push.apply(results, newContext.querySelectorAll(newSelector)); + return results; + } catch(qsaError) { + }finally{ + + if(!old){ + + context.removeAttribute("id"); + }; + }; + }; + }; + }; + // All others + return select(selector.replace(rtrim, "$1"), context, results, seed); + }; + /** + * Create key-value caches of limited size + * @return {Function} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ + function createCache(){ + + var keys = []; + function cache(key, value){ + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if(keys.push(key + " ") > Expr.cacheLength){ + + // Only keep the most recent entries + delete cache[keys.shift()]; + }; + return (cache[key + " "] = value); + }; + return cache; + }; + /** + * Mark a function for special use by Sizzle + * @param fn {Function} The function to mark + */ + function markFunction(fn){ + + fn[expando] = true; + return fn; + }; + /** + * Support testing using an element + * @param fn {Function} Passed the created div and expects a boolean result + */ + function assert(fn){ + + var div = document.createElement("div"); + try{ + + return !!fn(div); + } catch(e) { + + return false; + }finally{ + + // Remove from its parent by default + if(div.parentNode){ + + div.parentNode.removeChild(div); + }; + // release memory in IE + div = null; + }; + }; + /** + * Adds the same handler for all of the specified attrs + * @param attrs {String} Pipe-separated list of attributes + * @param handler {Function} The method that will be applied + */ + function addHandle(attrs, handler){ + + var arr = attrs.split("|"),i = attrs.length; + while(i--){ + + Expr.attrHandle[arr[i]] = handler; + }; + }; + /** + * Checks document order of two siblings + * @param a {Element} + * @param b {Element} + * @return {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ + function siblingCheck(a, b){ + + var cur = b && a,diff = cur && a.nodeType === 1 && b.nodeType === 1 && (~b.sourceIndex || MAX_NEGATIVE) - (~a.sourceIndex || MAX_NEGATIVE); + // Use IE sourceIndex if available on both nodes + if(diff){ + + return diff; + }; + // Check if b follows a + if(cur){ + + while((cur = cur.nextSibling)){ + + if(cur === b){ + + return -1; + }; + }; + }; + return a ? 1 : -1; + }; + /** + * Returns a function to use in pseudos for input types + * @param type {String} + */ + function createInputPseudo(type){ + + return function(elem){ + + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; + }; + /** + * Returns a function to use in pseudos for buttons + * @param type {String} + */ + function createButtonPseudo(type){ + + return function(elem){ + + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; + }; + /** + * Returns a function to use in pseudos for positionals + * @param fn {Function} + */ + function createPositionalPseudo(fn){ + + return markFunction(function(argument){ + + argument = +argument; + return markFunction(function(seed, matches){ + + var j,matchIndexes = fn([], seed.length, argument),i = matchIndexes.length; + // Match elements found at the specified indexes + while(i--){ + + if(seed[(j = matchIndexes[i])]){ + + seed[j] = !(matches[j] = seed[j]); + }; + }; + }); + }); + }; + /** + * Checks a node for validity as a Sizzle context + * @param context {Element|Object} + * @return {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ + function testContext(context){ + + return context && typeof context.getElementsByTagName !== strundefined && context; + }; + // Expose support vars for convenience + support = Sizzle.support = { + }; + /** + * Detects XML nodes + * @param elem {Element|Object} An element or a document + * @return {Boolean} True iff elem is a non-HTML XML node + */ + isXML = Sizzle.isXML = function(elem){ + + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; + }; + /** + * Sets document-related variables once based on the current document + * @param doc {Element|Object?} An element or document object to use to set the document + * @return {Object} Returns the current document + */ + setDocument = Sizzle.setDocument = function(node){ + + var hasCompare,doc = node ? node.ownerDocument || node : preferredDoc,parent = doc.defaultView; + // If no document and documentElement is available, return + if(doc === document || doc.nodeType !== 9 || !doc.documentElement){ + + return document; + }; + // Set our document + document = doc; + docElem = doc.documentElement; + // Support tests + documentIsHTML = !isXML(doc); + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if(parent && parent !== parent.top){ + + // IE11 does not have attachEvent, so all must suffer + if(parent.addEventListener){ + + parent.addEventListener("unload", function(){ + + setDocument(); + }, false); + } else if(parent.attachEvent){ + + parent.attachEvent("onunload", function(){ + + setDocument(); + }); + }; + }; + /* Attributes + ---------------------------------------------------------------------- */ + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function(div){ + + div.className = "i"; + return !div.getAttribute("className"); + }); + /* getElement(s)By* + ---------------------------------------------------------------------- */ + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function(div){ + + div.appendChild(doc.createComment("")); + return !div.getElementsByTagName("*").length; + }); + // Check if getElementsByClassName can be trusted + support.getElementsByClassName = rnative.test(doc.getElementsByClassName) && assert(function(div){ + + div.innerHTML = "
"; + // Support: Safari<4 + // Catch class over-caching + div.firstChild.className = "i"; + // Support: Opera<10 + // Catch gEBCN failure to find non-leading classes + return div.getElementsByClassName("i").length === 2; + }); + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function(div){ + + docElem.appendChild(div).id = expando; + return !doc.getElementsByName || !doc.getElementsByName(expando).length; + }); + // ID find and filter + if(support.getById){ + + Expr.find["ID"] = function(id, context){ + + if(typeof context.getElementById !== strundefined && documentIsHTML){ + + var m = context.getElementById(id); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + }; + }; + Expr.filter["ID"] = function(id){ + + var attrId = id.replace(runescape, funescape); + return function(elem){ + + return elem.getAttribute("id") === attrId; + }; + }; + } else { + + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + Expr.filter["ID"] = function(id){ + + var attrId = id.replace(runescape, funescape); + return function(elem){ + + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + }; + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? function(tag, context){ + + if(typeof context.getElementsByTagName !== strundefined){ + + return context.getElementsByTagName(tag); + }; + } : function(tag, context){ + + var elem,tmp = [],i = 0,results = context.getElementsByTagName(tag); + // Filter out possible comments + if(tag === "*"){ + + while((elem = results[i++])){ + + if(elem.nodeType === 1){ + + tmp.push(elem); + }; + }; + return tmp; + }; + return results; + }; + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function(className, context){ + + if(typeof context.getElementsByClassName !== strundefined && documentIsHTML){ + + return context.getElementsByClassName(className); + }; + }; + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + // QSA and matchesSelector support + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + if((support.qsa = rnative.test(doc.querySelectorAll))){ + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function(div){ + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + // Support: IE8, Opera 10-12 + // Nothing should be selected when empty strings follow ^= or $= or *= + if(div.querySelectorAll("[t^='']").length){ + + rbuggyQSA.push("[*^$]=" + whitespace + "*(?:''|\"\")"); + }; + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if(!div.querySelectorAll("[selected]").length){ + + rbuggyQSA.push("\\[" + whitespace + "*(?:value|" + booleans + ")"); + }; + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if(!div.querySelectorAll(":checked").length){ + + rbuggyQSA.push(":checked"); + }; + }); + assert(function(div){ + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute("type", "hidden"); + div.appendChild(input).setAttribute("name", "D"); + // Support: IE8 + // Enforce case-sensitivity of name attribute + if(div.querySelectorAll("[name=d]").length){ + + rbuggyQSA.push("name" + whitespace + "*[*^$|!~]?="); + }; + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if(!div.querySelectorAll(":enabled").length){ + + rbuggyQSA.push(":enabled", ":disabled"); + }; + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + }; + if((support.matchesSelector = rnative.test((matches = docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector)))){ + + assert(function(div){ + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call(div, "div"); + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call(div, "[s!='']:x"); + rbuggyMatches.push("!=", pseudos); + }); + }; + rbuggyQSA = rbuggyQSA.length && new RegExp(rbuggyQSA.join("|")); + rbuggyMatches = rbuggyMatches.length && new RegExp(rbuggyMatches.join("|")); + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test(docElem.compareDocumentPosition); + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = hasCompare || rnative.test(docElem.contains) ? function(a, b){ + + var adown = a.nodeType === 9 ? a.documentElement : a,bup = b && b.parentNode; + return a === bup || !!(bup && bup.nodeType === 1 && (adown.contains ? adown.contains(bup) : a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16)); + } : function(a, b){ + + if(b){ + + while((b = b.parentNode)){ + + if(b === a){ + + return true; + }; + }; + }; + return false; + }; + /* Sorting + ---------------------------------------------------------------------- */ + // Document order sorting + sortOrder = hasCompare ? function(a, b){ + + // Flag for duplicate removal + if(a === b){ + + hasDuplicate = true; + return 0; + }; + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if(compare){ + + return compare; + }; + // Calculate position if both inputs belong to the same document + compare = (a.ownerDocument || a) === (b.ownerDocument || b) ? a.compareDocumentPosition(b) : // Otherwise we know they are disconnected + 1; + // Disconnected nodes + if(compare & 1 || (!support.sortDetached && b.compareDocumentPosition(a) === compare)){ + + // Choose the first element that is related to our preferred document + if(a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a)){ + + return -1; + }; + if(b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b)){ + + return 1; + }; + // Maintain original order + return sortInput ? (indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) : 0; + }; + return compare & 4 ? -1 : 1; + } : function(a, b){ + + // Exit early if the nodes are identical + if(a === b){ + + hasDuplicate = true; + return 0; + }; + var cur,i = 0,aup = a.parentNode,bup = b.parentNode,ap = [a],bp = [b]; + // Parentless nodes are either documents or disconnected + if(!aup || !bup){ + + return a === doc ? -1 : b === doc ? 1 : aup ? -1 : bup ? 1 : sortInput ? (indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) : 0; + } else if(aup === bup){ + + return siblingCheck(a, b); + }; + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while((cur = cur.parentNode)){ + + ap.unshift(cur); + }; + cur = b; + while((cur = cur.parentNode)){ + + bp.unshift(cur); + }; + // Walk down the tree looking for a discrepancy + while(ap[i] === bp[i]){ + + i++; + }; + return i ? // Do a sibling check if the nodes have a common ancestor + siblingCheck(ap[i], bp[i]) : // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : bp[i] === preferredDoc ? 1 : 0; + }; + return doc; + }; + Sizzle.matches = function(expr, elements){ + + return Sizzle(expr, null, null, elements); + }; + Sizzle.matchesSelector = function(elem, expr){ + + // Set document vars if needed + if((elem.ownerDocument || elem) !== document){ + + setDocument(elem); + }; + // Make sure that attribute selectors are quoted + expr = expr.replace(rattributeQuotes, "='$1']"); + if(support.matchesSelector && documentIsHTML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && (!rbuggyQSA || !rbuggyQSA.test(expr))){ + + try{ + + var ret = matches.call(elem, expr); + // IE 9's matchesSelector returns false on disconnected nodes + if(ret || support.disconnectedMatch || // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11){ + + return ret; + }; + } catch(e) { + }; + }; + return Sizzle(expr, document, null, [elem]).length > 0; + }; + Sizzle.contains = function(context, elem){ + + // Set document vars if needed + if((context.ownerDocument || context) !== document){ + + setDocument(context); + }; + return contains(context, elem); + }; + Sizzle.attr = function(elem, name){ + + // Set document vars if needed + if((elem.ownerDocument || elem) !== document){ + + setDocument(elem); + }; + var fn = Expr.attrHandle[name.toLowerCase()],// Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call(Expr.attrHandle, name.toLowerCase()) ? fn(elem, name, !documentIsHTML) : undefined; + return val !== undefined ? val : support.attributes || !documentIsHTML ? elem.getAttribute(name) : (val = elem.getAttributeNode(name)) && val.specified ? val.value : null; + }; + Sizzle.error = function(msg){ + + throw new Error("Syntax error, unrecognized expression: " + msg); + }; + /** + * Document sorting and removing duplicates + * @param results {ArrayLike} + */ + Sizzle.uniqueSort = function(results){ + + var elem,duplicates = [],j = 0,i = 0; + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice(0); + results.sort(sortOrder); + if(hasDuplicate){ + + while((elem = results[i++])){ + + if(elem === results[i]){ + + j = duplicates.push(i); + }; + }; + while(j--){ + + results.splice(duplicates[j], 1); + }; + }; + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + return results; + }; + /** + * Utility function for retrieving the text value of an array of DOM nodes + * @param elem {Array|Element} + */ + getText = Sizzle.getText = function(elem){ + + var node,ret = "",i = 0,nodeType = elem.nodeType; + if(!nodeType){ + + // If no nodeType, this is expected to be an array + while((node = elem[i++])){ + + // Do not traverse comment nodes + ret += getText(node); + }; + } else if(nodeType === 1 || nodeType === 9 || nodeType === 11){ + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if(typeof elem.textContent === "string"){ + + return elem.textContent; + } else { + + // Traverse its children + for(elem = elem.firstChild;elem;elem = elem.nextSibling){ + + ret += getText(elem); + }; + }; + } else if(nodeType === 3 || nodeType === 4){ + + return elem.nodeValue; + };; + // Do not include comment or processing instruction nodes + return ret; + }; + Expr = Sizzle.selectors = { + // Can be adjusted by the user + cacheLength : 50, + createPseudo : markFunction, + match : matchExpr, + attrHandle : { + }, + find : { + }, + relative : { + ">" : { + dir : "parentNode", + first : true + }, + " " : { + dir : "parentNode" + }, + "+" : { + dir : "previousSibling", + first : true + }, + "~" : { + dir : "previousSibling" + } + }, + preFilter : { + "ATTR" : function(match){ + + match[1] = match[1].replace(runescape, funescape); + // Move the given value to match[3] whether quoted or unquoted + match[3] = (match[4] || match[5] || "").replace(runescape, funescape); + if(match[2] === "~="){ + + match[3] = " " + match[3] + " "; + }; + return match.slice(0, 4); + }, + "CHILD" : function(match){ + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + if(match[1].slice(0, 3) === "nth"){ + + // nth-* requires argument + if(!match[3]){ + + Sizzle.error(match[0]); + }; + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +(match[4] ? match[5] + (match[6] || 1) : 2 * (match[3] === "even" || match[3] === "odd")); + match[5] = +((match[7] + match[8]) || match[3] === "odd"); + } else if(match[3]){ + + Sizzle.error(match[0]); + }; + return match; + }, + "PSEUDO" : function(match){ + + var excess,unquoted = !match[5] && match[2]; + if(matchExpr["CHILD"].test(match[0])){ + + return null; + }; + // Accept quoted arguments as-is + if(match[3] && match[4] !== undefined){ + + match[2] = match[4]; + } else if(unquoted && rpseudo.test(unquoted) && // Get excess from tokenize (recursively) + (excess = tokenize(unquoted, true)) && // advance to the next closing parenthesis + (excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)){ + + // excess is a negative index + match[0] = match[0].slice(0, excess); + match[2] = unquoted.slice(0, excess); + }; + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice(0, 3); + } + }, + filter : { + "TAG" : function(nodeNameSelector){ + + var nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase(); + return nodeNameSelector === "*" ? function(){ + + return true; + } : function(elem){ + + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + "CLASS" : function(className){ + + var pattern = classCache[className + " "]; + return pattern || (pattern = new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)")) && classCache(className, function(elem){ + + return pattern.test(typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || ""); + }); + }, + "ATTR" : function(name, operator, check){ + + return function(elem){ + + var result = Sizzle.attr(elem, name); + if(result == null){ + + return operator === "!="; + }; + if(!operator){ + + return true; + }; + result += ""; + return operator === "=" ? result === check : operator === "!=" ? result !== check : operator === "^=" ? check && result.indexOf(check) === 0 : operator === "*=" ? check && result.indexOf(check) > -1 : operator === "$=" ? check && result.slice(-check.length) === check : operator === "~=" ? (" " + result + " ").indexOf(check) > -1 : operator === "|=" ? result === check || result.slice(0, check.length + 1) === check + "-" : false; + }; + }, + "CHILD" : function(type, what, argument, first, last){ + + var simple = type.slice(0, 3) !== "nth",forward = type.slice(-4) !== "last",ofType = what === "of-type"; + return first === 1 && last === 0 ? // Shortcut for :nth-*(n) + function(elem){ + + return !!elem.parentNode; + } : function(elem, context, xml){ + + var cache,outerCache,node,diff,nodeIndex,start,dir = simple !== forward ? "nextSibling" : "previousSibling",parent = elem.parentNode,name = ofType && elem.nodeName.toLowerCase(),useCache = !xml && !ofType; + if(parent){ + + // :(first|last|only)-(child|of-type) + if(simple){ + + while(dir){ + + node = elem; + while((node = node[dir])){ + + if(ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1){ + + return false; + }; + }; + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + }; + return true; + }; + start = [forward ? parent.firstChild : parent.lastChild]; + // non-xml :nth-child(...) stores cache data on `parent` + if(forward && useCache){ + + // Seek `elem` from a previously-cached index + outerCache = parent[expando] || (parent[expando] = { + }); + cache = outerCache[type] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[nodeIndex]; + while((node = ++nodeIndex && node && node[dir] || // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop())){ + + // When found, cache indexes on `parent` and break + if(node.nodeType === 1 && ++diff && node === elem){ + + outerCache[type] = [dirruns, nodeIndex, diff]; + break; + }; + }; + } else if(useCache && (cache = (elem[expando] || (elem[expando] = { + }))[type]) && cache[0] === dirruns){ + + diff = cache[1]; + } else { + + // Use the same loop as above to seek `elem` from the start + while((node = ++nodeIndex && node && node[dir] || (diff = nodeIndex = 0) || start.pop())){ + + if((ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) && ++diff){ + + // Cache the index of each encountered element + if(useCache){ + + (node[expando] || (node[expando] = { + }))[type] = [dirruns, diff]; + }; + if(node === elem){ + + break; + }; + }; + }; + }; + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || (diff % first === 0 && diff / first >= 0); + }; + }; + }, + "PSEUDO" : function(pseudo, argument){ + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args,fn = Expr.pseudos[pseudo] || Expr.setFilters[pseudo.toLowerCase()] || Sizzle.error("unsupported pseudo: " + pseudo); + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if(fn[expando]){ + + return fn(argument); + }; + // But maintain support for old signatures + if(fn.length > 1){ + + args = [pseudo, pseudo, "", argument]; + return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase()) ? markFunction(function(seed, matches){ + + var idx,matched = fn(seed, argument),i = matched.length; + while(i--){ + + idx = indexOf.call(seed, matched[i]); + seed[idx] = !(matches[idx] = matched[i]); + }; + }) : function(elem){ + + return fn(elem, 0, args); + }; + }; + return fn; + } + }, + pseudos : { + // Potentially complex pseudos + "not" : markFunction(function(selector){ + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [],results = [],matcher = compile(selector.replace(rtrim, "$1")); + return matcher[expando] ? markFunction(function(seed, matches, context, xml){ + + var elem,unmatched = matcher(seed, null, xml, []),i = seed.length; + // Match elements unmatched by `matcher` + while(i--){ + + if((elem = unmatched[i])){ + + seed[i] = !(matches[i] = elem); + }; + }; + }) : function(elem, context, xml){ + + input[0] = elem; + matcher(input, null, xml, results); + return !results.pop(); + }; + }), + "has" : markFunction(function(selector){ + + return function(elem){ + + return Sizzle(selector, elem).length > 0; + }; + }), + "contains" : markFunction(function(text){ + + return function(elem){ + + return (elem.textContent || elem.innerText || getText(elem)).indexOf(text) > -1; + }; + }), + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang" : markFunction(function(lang){ + + // lang value must be a valid identifier + if(!ridentifier.test(lang || "")){ + + Sizzle.error("unsupported lang: " + lang); + }; + lang = lang.replace(runescape, funescape).toLowerCase(); + return function(elem){ + + var elemLang; + do { + + if((elemLang = documentIsHTML ? elem.lang : elem.getAttribute("xml:lang") || elem.getAttribute("lang"))){ + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf(lang + "-") === 0; + }; + }while(((elem = elem.parentNode) && elem.nodeType === 1)); + return false; + }; + }), + // Miscellaneous + "target" : function(elem){ + + var hash = window.location && window.location.hash; + return hash && hash.slice(1) === elem.id; + }, + "root" : function(elem){ + + return elem === docElem; + }, + "focus" : function(elem){ + + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + // Boolean properties + "enabled" : function(elem){ + + return elem.disabled === false; + }, + "disabled" : function(elem){ + + return elem.disabled === true; + }, + "checked" : function(elem){ + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + "selected" : function(elem){ + + // Accessing this property makes selected-by-default + // options in Safari work properly + if(elem.parentNode){ + + elem.parentNode.selectedIndex; + }; + return elem.selected === true; + }, + // Contents + "empty" : function(elem){ + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for(elem = elem.firstChild;elem;elem = elem.nextSibling){ + + if(elem.nodeType < 6){ + + return false; + }; + }; + return true; + }, + "parent" : function(elem){ + + return !Expr.pseudos["empty"](elem); + }, + // Element/input types + "header" : function(elem){ + + return rheader.test(elem.nodeName); + }, + "input" : function(elem){ + + return rinputs.test(elem.nodeName); + }, + "button" : function(elem){ + + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + "text" : function(elem){ + + var attr; + return elem.nodeName.toLowerCase() === "input" && elem.type === "text" && // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ((attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text"); + }, + // Position-in-collection + "first" : createPositionalPseudo(function(){ + + return [0]; + }), + "last" : createPositionalPseudo(function(matchIndexes, length){ + + return [length - 1]; + }), + "eq" : createPositionalPseudo(function(matchIndexes, length, argument){ + + return [argument < 0 ? argument + length : argument]; + }), + "even" : createPositionalPseudo(function(matchIndexes, length){ + + var i = 0; + for(;i < length;i += 2){ + + matchIndexes.push(i); + }; + return matchIndexes; + }), + "odd" : createPositionalPseudo(function(matchIndexes, length){ + + var i = 1; + for(;i < length;i += 2){ + + matchIndexes.push(i); + }; + return matchIndexes; + }), + "lt" : createPositionalPseudo(function(matchIndexes, length, argument){ + + var i = argument < 0 ? argument + length : argument; + for(;--i >= 0;){ + + matchIndexes.push(i); + }; + return matchIndexes; + }), + "gt" : createPositionalPseudo(function(matchIndexes, length, argument){ + + var i = argument < 0 ? argument + length : argument; + for(;++i < length;){ + + matchIndexes.push(i); + }; + return matchIndexes; + }) + } + }; + Expr.pseudos["nth"] = Expr.pseudos["eq"]; + // Add button/input type pseudos + for(i in { + radio : true, + checkbox : true, + file : true, + password : true, + image : true + }){ + + Expr.pseudos[i] = createInputPseudo(i); + }; + for(i in { + submit : true, + reset : true + }){ + + Expr.pseudos[i] = createButtonPseudo(i); + }; + // Easy API for creating new setFilters + function setFilters(){ + }; + setFilters.prototype = Expr.filters = Expr.pseudos; + Expr.setFilters = new setFilters(); + function tokenize(selector, parseOnly){ + + var matched,match,tokens,type,soFar,groups,preFilters,cached = tokenCache[selector + " "]; + if(cached){ + + return parseOnly ? 0 : cached.slice(0); + }; + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + while(soFar){ + + // Comma and first run + if(!matched || (match = rcomma.exec(soFar))){ + + if(match){ + + // Don't consume trailing commas as valid + soFar = soFar.slice(match[0].length) || soFar; + }; + groups.push((tokens = [])); + }; + matched = false; + // Combinators + if((match = rcombinators.exec(soFar))){ + + matched = match.shift(); + tokens.push({ + value : matched, + // Cast descendant combinators to space + type : match[0].replace(rtrim, " ") + }); + soFar = soFar.slice(matched.length); + }; + // Filters + for(type in Expr.filter){ + + if((match = matchExpr[type].exec(soFar)) && (!preFilters[type] || (match = preFilters[type](match)))){ + + matched = match.shift(); + tokens.push({ + value : matched, + type : type, + matches : match + }); + soFar = soFar.slice(matched.length); + }; + }; + if(!matched){ + + break; + }; + }; + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) : // Cache the tokens + tokenCache(selector, groups).slice(0); + }; + function toSelector(tokens){ + + var i = 0,len = tokens.length,selector = ""; + for(;i < len;i++){ + + selector += tokens[i].value; + }; + return selector; + }; + function addCombinator(matcher, combinator, base){ + + var dir = combinator.dir,checkNonElements = base && dir === "parentNode",doneName = done++; + return combinator.first ? // Check against closest ancestor/preceding element + function(elem, context, xml){ + + while((elem = elem[dir])){ + + if(elem.nodeType === 1 || checkNonElements){ + + return matcher(elem, context, xml); + }; + }; + } : // Check against all ancestor/preceding elements + function(elem, context, xml){ + + var oldCache,outerCache,newCache = [dirruns, doneName]; + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if(xml){ + + while((elem = elem[dir])){ + + if(elem.nodeType === 1 || checkNonElements){ + + if(matcher(elem, context, xml)){ + + return true; + }; + }; + }; + } else { + + while((elem = elem[dir])){ + + if(elem.nodeType === 1 || checkNonElements){ + + outerCache = elem[expando] || (elem[expando] = { + }); + if((oldCache = outerCache[dir]) && oldCache[0] === dirruns && oldCache[1] === doneName){ + + // Assign to newCache so results back-propagate to previous elements + return (newCache[2] = oldCache[2]); + } else { + + // Reuse newcache so results back-propagate to previous elements + outerCache[dir] = newCache; + // A match means we're done; a fail means we have to keep checking + if((newCache[2] = matcher(elem, context, xml))){ + + return true; + }; + }; + }; + }; + }; + }; + }; + function elementMatcher(matchers){ + + return matchers.length > 1 ? function(elem, context, xml){ + + var i = matchers.length; + while(i--){ + + if(!matchers[i](elem, context, xml)){ + + return false; + }; + }; + return true; + } : matchers[0]; + }; + function multipleContexts(selector, contexts, results){ + + var i = 0,len = contexts.length; + for(;i < len;i++){ + + Sizzle(selector, contexts[i], results); + }; + return results; + }; + function condense(unmatched, map, filter, context, xml){ + + var elem,newUnmatched = [],i = 0,len = unmatched.length,mapped = map != null; + for(;i < len;i++){ + + if((elem = unmatched[i])){ + + if(!filter || filter(elem, context, xml)){ + + newUnmatched.push(elem); + if(mapped){ + + map.push(i); + }; + }; + }; + }; + return newUnmatched; + }; + function setMatcher(preFilter, selector, matcher, postFilter, postFinder, postSelector){ + + if(postFilter && !postFilter[expando]){ + + postFilter = setMatcher(postFilter); + }; + if(postFinder && !postFinder[expando]){ + + postFinder = setMatcher(postFinder, postSelector); + }; + return markFunction(function(seed, results, context, xml){ + + var temp,i,elem,preMap = [],postMap = [],preexisting = results.length,// Get initial elements from seed or context + elems = seed || multipleContexts(selector || "*", context.nodeType ? [context] : context, []),// Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && (seed || !selector) ? condense(elems, preMap, preFilter, context, xml) : elems,matcherOut = matcher ? // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || (seed ? preFilter : preexisting || postFilter) ? // ...intermediate processing is necessary + [] : // ...otherwise use results directly + results : matcherIn; + // Find primary matches + if(matcher){ + + matcher(matcherIn, matcherOut, context, xml); + }; + // Apply postFilter + if(postFilter){ + + temp = condense(matcherOut, postMap); + postFilter(temp, [], context, xml); + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while(i--){ + + if((elem = temp[i])){ + + matcherOut[postMap[i]] = !(matcherIn[postMap[i]] = elem); + }; + }; + }; + if(seed){ + + if(postFinder || preFilter){ + + if(postFinder){ + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while(i--){ + + if((elem = matcherOut[i])){ + + // Restore matcherIn since elem is not yet a final match + temp.push((matcherIn[i] = elem)); + }; + }; + postFinder(null, (matcherOut = []), temp, xml); + }; + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while(i--){ + + if((elem = matcherOut[i]) && (temp = postFinder ? indexOf.call(seed, elem) : preMap[i]) > -1){ + + seed[temp] = !(results[temp] = elem); + }; + }; + }; + } else { + + matcherOut = condense(matcherOut === results ? matcherOut.splice(preexisting, matcherOut.length) : matcherOut); + if(postFinder){ + + postFinder(null, results, matcherOut, xml); + } else { + + push.apply(results, matcherOut); + }; + }; + }); + }; + function matcherFromTokens(tokens){ + + var checkContext,matcher,j,len = tokens.length,leadingRelative = Expr.relative[tokens[0].type],implicitRelative = leadingRelative || Expr.relative[" "],i = leadingRelative ? 1 : 0,// The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator(function(elem){ + + return elem === checkContext; + }, implicitRelative, true),matchAnyContext = addCombinator(function(elem){ + + return indexOf.call(checkContext, elem) > -1; + }, implicitRelative, true),matchers = [function(elem, context, xml){ + + return (!leadingRelative && (xml || context !== outermostContext)) || ((checkContext = context).nodeType ? matchContext(elem, context, xml) : matchAnyContext(elem, context, xml)); + }]; + for(;i < len;i++){ + + if((matcher = Expr.relative[tokens[i].type])){ + + matchers = [addCombinator(elementMatcher(matchers), matcher)]; + } else { + + matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches); + // Return special upon seeing a positional matcher + if(matcher[expando]){ + + // Find the next relative operator (if any) for proper handling + j = ++i; + for(;j < len;j++){ + + if(Expr.relative[tokens[j].type]){ + + break; + }; + }; + return setMatcher(i > 1 && elementMatcher(matchers), i > 1 && toSelector(// If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice(0, i - 1).concat({ + value : tokens[i - 2].type === " " ? "*" : "" + })).replace(rtrim, "$1"), matcher, i < j && matcherFromTokens(tokens.slice(i, j)), j < len && matcherFromTokens((tokens = tokens.slice(j))), j < len && toSelector(tokens)); + }; + matchers.push(matcher); + }; + }; + return elementMatcher(matchers); + }; + function matcherFromGroupMatchers(elementMatchers, setMatchers){ + + var bySet = setMatchers.length > 0,byElement = elementMatchers.length > 0,superMatcher = function(seed, context, xml, results, outermost){ + + var elem,j,matcher,matchedCount = 0,i = "0",unmatched = seed && [],setMatched = [],contextBackup = outermostContext,// We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]("*", outermost),// Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),len = elems.length; + if(outermost){ + + outermostContext = context !== document && context; + }; + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for(;i !== len && (elem = elems[i]) != null;i++){ + + if(byElement && elem){ + + j = 0; + while((matcher = elementMatchers[j++])){ + + if(matcher(elem, context, xml)){ + + results.push(elem); + break; + }; + }; + if(outermost){ + + dirruns = dirrunsUnique; + }; + }; + // Track unmatched elements for set filters + if(bySet){ + + // They will have gone through all possible matchers + if((elem = !matcher && elem)){ + + matchedCount--; + }; + // Lengthen the array for every element, matched or not + if(seed){ + + unmatched.push(elem); + }; + }; + }; + // Apply set filters to unmatched elements + matchedCount += i; + if(bySet && i !== matchedCount){ + + j = 0; + while((matcher = setMatchers[j++])){ + + matcher(unmatched, setMatched, context, xml); + }; + if(seed){ + + // Reintegrate element matches to eliminate the need for sorting + if(matchedCount > 0){ + + while(i--){ + + if(!(unmatched[i] || setMatched[i])){ + + setMatched[i] = pop.call(results); + }; + }; + }; + // Discard index placeholder values to get only actual matches + setMatched = condense(setMatched); + }; + // Add matches to results + push.apply(results, setMatched); + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if(outermost && !seed && setMatched.length > 0 && (matchedCount + setMatchers.length) > 1){ + + Sizzle.uniqueSort(results); + }; + }; + // Override manipulation of globals by nested matchers + if(outermost){ + + dirruns = dirrunsUnique; + outermostContext = contextBackup; + }; + return unmatched; + }; + return bySet ? markFunction(superMatcher) : superMatcher; + }; + compile = Sizzle.compile = function(selector, match){ + + var i,setMatchers = [],elementMatchers = [],cached = compilerCache[selector + " "]; + if(!cached){ + + // Generate a function of recursive functions that can be used to check each element + if(!match){ + + match = tokenize(selector); + }; + i = match.length; + while(i--){ + + cached = matcherFromTokens(match[i]); + if(cached[expando]){ + + setMatchers.push(cached); + } else { + + elementMatchers.push(cached); + }; + }; + // Cache the compiled function + cached = compilerCache(selector, matcherFromGroupMatchers(elementMatchers, setMatchers)); + // Save selector and tokenization + cached.selector = selector; + }; + return cached; + }; + /** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param selector {String|Function} A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param context {Element} + * @param results {Array?} + * @param seed {Array?} A set of elements to match against + */ + select = Sizzle.select = function(selector, context, results, seed){ + + var i,tokens,token,type,find,compiled = typeof selector === "function" && selector,match = !seed && tokenize((selector = compiled.selector || selector)); + results = results || []; + // Try to minimize operations if there is no seed and only one group + if(match.length === 1){ + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice(0); + if(tokens.length > 2 && (token = tokens[0]).type === "ID" && support.getById && context.nodeType === 9 && documentIsHTML && Expr.relative[tokens[1].type]){ + + context = (Expr.find["ID"](token.matches[0].replace(runescape, funescape), context) || [])[0]; + if(!context){ + + return results; + } else if(compiled){ + + context = context.parentNode; + }; + selector = selector.slice(tokens.shift().value.length); + }; + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test(selector) ? 0 : tokens.length; + while(i--){ + + token = tokens[i]; + // Abort if we hit a combinator + if(Expr.relative[(type = token.type)]){ + + break; + }; + if((find = Expr.find[type])){ + + // Search, expanding context for leading sibling combinators + if((seed = find(token.matches[0].replace(runescape, funescape), rsibling.test(tokens[0].type) && testContext(context.parentNode) || context))){ + + // If seed is empty or no tokens remain, we can return early + tokens.splice(i, 1); + selector = seed.length && toSelector(tokens); + if(!selector){ + + push.apply(results, seed); + return results; + }; + break; + }; + }; + }; + }; + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + (compiled || compile(selector, match))(seed, context, !documentIsHTML, results, rsibling.test(selector) && testContext(context.parentNode) || context); + return results; + }; + // One-time assignments + // Sort stability + support.sortStable = expando.split("").sort(sortOrder).join("") === expando; + // Support: Chrome<14 + // Always assume duplicates if they aren't passed to the comparison function + support.detectDuplicates = !!hasDuplicate; + // Initialize against the default document + setDocument(); + // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) + // Detached nodes confoundingly follow *each other* + support.sortDetached = assert(function(div1){ + + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition(document.createElement("div")) & 1; + }); + // Support: IE<8 + // Prevent attribute/property "interpolation" + // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx + if(!assert(function(div){ + + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#"; + })){ + + addHandle("type|href|height|width", function(elem, name, isXML){ + + if(!isXML){ + + return elem.getAttribute(name, name.toLowerCase() === "type" ? 1 : 2); + }; + }); + }; + // Support: IE<9 + // Use defaultValue in place of getAttribute("value") + if(!support.attributes || !assert(function(div){ + + div.innerHTML = ""; + div.firstChild.setAttribute("value", ""); + return div.firstChild.getAttribute("value") === ""; + })){ + + addHandle("value", function(elem, name, isXML){ + + if(!isXML && elem.nodeName.toLowerCase() === "input"){ + + return elem.defaultValue; + }; + }); + }; + // Support: IE<9 + // Use getAttributeNode to fetch booleans when getAttribute lies + if(!assert(function(div){ + + return div.getAttribute("disabled") == null; + })){ + + addHandle(booleans, function(elem, name, isXML){ + + var val; + if(!isXML){ + + return elem[name] === true ? name.toLowerCase() : (val = elem.getAttributeNode(name)) && val.specified ? val.value : null; + }; + }); + }; + // EXPOSE qooxdoo variant + qx.bom.Selector.query = function(selector, context){ + + return Sizzle(selector, context); + }; + qx.bom.Selector.matches = function(selector, set){ + + return Sizzle(selector, null, null, set); + }; +})(window); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2007-2008 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Fabian Jakobs (fjakobs) + +************************************************************************ */ +/** + * Utility class with type check for all native JavaScript data types. + */ +qx.Bootstrap.define("qx.lang.Type", { + statics : { + /** + * Get the internal class of the value. See + * http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/ + * for details. + * + * @signature function(value) + * @param value {var} value to get the class for + * @return {String} the internal class of the value + */ + getClass : qx.Bootstrap.getClass, + /** + * Whether the value is a string. + * + * @signature function(value) + * @param value {var} Value to check. + * @return {Boolean} Whether the value is a string. + */ + isString : qx.Bootstrap.isString, + /** + * Whether the value is an array. + * + * @signature function(value) + * @param value {var} Value to check. + * @return {Boolean} Whether the value is an array. + */ + isArray : qx.Bootstrap.isArray, + /** + * Whether the value is an object. Note that built-in types like Window are + * not reported to be objects. + * + * @signature function(value) + * @param value {var} Value to check. + * @return {Boolean} Whether the value is an object. + */ + isObject : qx.Bootstrap.isObject, + /** + * Whether the value is a function. + * + * @signature function(value) + * @param value {var} Value to check. + * @return {Boolean} Whether the value is a function. + */ + isFunction : qx.Bootstrap.isFunction, + /** + * Whether the value is a regular expression. + * + * @param value {var} Value to check. + * @return {Boolean} Whether the value is a regular expression. + */ + isRegExp : function(value){ + + return this.getClass(value) == "RegExp"; + }, + /** + * Whether the value is a number. + * + * @param value {var} Value to check. + * @return {Boolean} Whether the value is a number. + */ + isNumber : function(value){ + + // Added "value !== null" because IE throws an exception "Object expected" + // by executing "value instanceof Number" if value is a DOM element that + // doesn't exist. It seems that there is an internal different between a + // JavaScript null and a null returned from calling DOM. + // e.q. by document.getElementById("ReturnedNull"). + return (value !== null && (this.getClass(value) == "Number" || value instanceof Number)); + }, + /** + * Whether the value is a boolean. + * + * @param value {var} Value to check. + * @return {Boolean} Whether the value is a boolean. + */ + isBoolean : function(value){ + + // Added "value !== null" because IE throws an exception "Object expected" + // by executing "value instanceof Boolean" if value is a DOM element that + // doesn't exist. It seems that there is an internal different between a + // JavaScript null and a null returned from calling DOM. + // e.q. by document.getElementById("ReturnedNull"). + return (value !== null && (this.getClass(value) == "Boolean" || value instanceof Boolean)); + }, + /** + * Whether the value is a date. + * + * @param value {var} Value to check. + * @return {Boolean} Whether the value is a date. + */ + isDate : function(value){ + + // Added "value !== null" because IE throws an exception "Object expected" + // by executing "value instanceof Date" if value is a DOM element that + // doesn't exist. It seems that there is an internal different between a + // JavaScript null and a null returned from calling DOM. + // e.q. by document.getElementById("ReturnedNull"). + return (value !== null && (this.getClass(value) == "Date" || value instanceof Date)); + }, + /** + * Whether the value is a Error. + * + * @param value {var} Value to check. + * @return {Boolean} Whether the value is a Error. + */ + isError : function(value){ + + // Added "value !== null" because IE throws an exception "Object expected" + // by executing "value instanceof Error" if value is a DOM element that + // doesn't exist. It seems that there is an internal different between a + // JavaScript null and a null returned from calling DOM. + // e.q. by document.getElementById("ReturnedNull"). + return (value !== null && (this.getClass(value) == "Error" || value instanceof Error)); + } + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2012 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Martin Wittemann (wittemann) + + ====================================================================== + + This class contains code based on the following work: + + * es5-shim + + Code: + https://github.com/kriskowal/es5-shim/ + + Copyright: + (c) 2009, 2010 Kristopher Michael Kowal + + License: + https://github.com/kriskowal/es5-shim/blob/master/LICENSE + + ---------------------------------------------------------------------- + + Copyright 2009, 2010 Kristopher Michael Kowal. All rights reserved. + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + + ---------------------------------------------------------------------- + + Version: + Snapshot taken on 2012-07-25,: + commit 9f539abd9aa9950e1d907077a4be7f5133a00e52 + +************************************************************************ */ +/** + * This class is responsible for the normalization of the native 'Function' object. + * It checks if these methods are available and, if not, appends them to + * ensure compatibility in all browsers. + * For usage samples, check out the attached links. + * + * @group (Polyfill) + */ +qx.Bootstrap.define("qx.lang.normalize.Function", { + statics : { + /** + * MDN documentation | + * Annotated ES5 Spec + * + * Example for the bind method: + *
+     * // sample code, assumes the used variables are already defined
+     *
+     * // the listener method demonstrates how to pass dynamic values
+     * // to a method using 'bind'
+     * var changeValueListener = function(value, event) {
+     *   // value is passed by the 'bind' method: its value is 'myArray[i]'
+     *   // second argument is passed by the 'on' method: its value is a event object
+     *   // 'this' is pointing to 'myComponent', since the first argument of 'bind' defines the context of the function call
+     * };
+     * var myArray = [ 0, 2, 4, 6 ];
+     * for (var i=0, j=myArray.length; i<j; i++) {
+     *   myComponent.on("changeValue", changeValueListener.bind(myComponent, myArray[i]));
+     * }
+     * 
+ * + * @param that {var?} Context for the bound function + * @return {Function} The bound function + */ + bind : function(that){ + + var slice = Array.prototype.slice; + // .length is 1 + // 1. Let Target be the this value. + var target = this; + // 2. If IsCallable(Target) is false, throw a TypeError exception. + if(typeof target != "function"){ + + throw new TypeError("Function.prototype.bind called on incompatible " + target); + }; + // 3. Let A be a new (possibly empty) internal list of all of the + // argument values provided after thisArg (arg1, arg2 etc), in order. + // XXX slicedArgs will stand in for "A" if used + var args = slice.call(arguments, 1); + // for normal call + // 4. Let F be a new native ECMAScript object. + // 11. Set the [[Prototype]] internal property of F to the standard + // built-in Function prototype object as specified in 15.3.3.1. + // 12. Set the [[Call]] internal property of F as described in + // 15.3.4.5.1. + // 13. Set the [[Construct]] internal property of F as described in + // 15.3.4.5.2. + // 14. Set the [[HasInstance]] internal property of F as described in + // 15.3.4.5.3. + var bound = function(){ + + if(this instanceof bound){ + + // 15.3.4.5.2 [[Construct]] + // When the [[Construct]] internal method of a function object, + // F that was created using the bind function is called with a + // list of arguments ExtraArgs, the following steps are taken: + // 1. Let target be the value of F's [[TargetFunction]] + // internal property. + // 2. If target has no [[Construct]] internal method, a + // TypeError exception is thrown. + // 3. Let boundArgs be the value of F's [[BoundArgs]] internal + // property. + // 4. Let args be a new list containing the same values as the + // list boundArgs in the same order followed by the same + // values as the list ExtraArgs in the same order. + // 5. Return the result of calling the [[Construct]] internal + // method of target providing args as the arguments. + var F = function(){ + }; + F.prototype = target.prototype; + var self = new F; + var result = target.apply(self, args.concat(slice.call(arguments))); + if(Object(result) === result){ + + return result; + }; + return self; + } else { + + // 15.3.4.5.1 [[Call]] + // When the [[Call]] internal method of a function object, F, + // which was created using the bind function is called with a + // this value and a list of arguments ExtraArgs, the following + // steps are taken: + // 1. Let boundArgs be the value of F's [[BoundArgs]] internal + // property. + // 2. Let boundThis be the value of F's [[BoundThis]] internal + // property. + // 3. Let target be the value of F's [[TargetFunction]] internal + // property. + // 4. Let args be a new list containing the same values as the + // list boundArgs in the same order followed by the same + // values as the list ExtraArgs in the same order. + // 5. Return the result of calling the [[Call]] internal method + // of target providing boundThis as the this value and + // providing args as the arguments. + // equiv: target.call(this, ...boundArgs, ...args) + return target.apply(that, args.concat(slice.call(arguments))); + }; + }; + // XXX bound.length is never writable, so don't even try + // + // 15. If the [[Class]] internal property of Target is "Function", then + // a. Let L be the length property of Target minus the length of A. + // b. Set the length own property of F to either 0 or L, whichever is + // larger. + // 16. Else set the length own property of F to 0. + // 17. Set the attributes of the length own property of F to the values + // specified in 15.3.5.1. + // (Not implemented but in the spec) + // 18. Set the [[Extensible]] internal property of F to true. + // (Not implemented but in the spec) + // 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3). + // 20. Call the [[DefineOwnProperty]] internal method of F with + // arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]: + // thrower, [[Enumerable]]: false, [[Configurable]]: false}, and + // false. + // 21. Call the [[DefineOwnProperty]] internal method of F with + // arguments "arguments", PropertyDescriptor {[[Get]]: thrower, + // [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false}, + // and false. + // NOTE Function objects created using Function.prototype.bind do not + // have a prototype property or the [[Code]], [[FormalParameters]], and + // [[Scope]] internal properties. + // XXX can't delete prototype in pure-js. + // 22. Return F. + return bound; + } + }, + defer : function(statics){ + + if(!qx.core.Environment.get("ecmascript.function.bind")){ + + Function.prototype.bind = statics.bind; + }; + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Sebastian Werner (wpbasti) + * Andreas Ecker (ecker) + +************************************************************************ */ +/** + * This class is used to define mixins (similar to mixins in Ruby). + * + * Mixins are collections of code and variables, which can be merged into + * other classes. They are similar to classes but don't support inheritance. + * + * See the description of the {@link #define} method how a mixin is defined. + * + * @require(qx.lang.normalize.Array) + */ +qx.Bootstrap.define("qx.Mixin", { + statics : { + /* + --------------------------------------------------------------------------- + PUBLIC API + --------------------------------------------------------------------------- + */ + /** + * Define a new mixin. + * + * Example: + *
+     * qx.Mixin.define("name",
+     * {
+     *   include: [SuperMixins],
+     *
+     *   properties: {
+     *     tabIndex: {type: "number", init: -1}
+     *   },
+     *
+     *   members:
+     *   {
+     *     prop1: "foo",
+     *     meth1: function() {},
+     *     meth2: function() {}
+     *   }
+     * });
+     * 
+ * + * @param name {String} name of the mixin + * @param config {Map ? null} Mixin definition structure. The configuration map has the following keys: + * + * + * + * + * + * + * + * + * + *
NameTypeDescription
constructFunctionAn optional mixin constructor. It is called on instantiation each + * class including this mixin. The constructor takes no parameters.
destructFunctionAn optional mixin destructor.
includeMixin[]Array of mixins, which will be merged into the mixin.
staticsMap + * Map of statics of the mixin. The statics will not get copied into the target class. They remain + * accessible from the mixin. This is the same behaviour as statics in interfaces ({@link qx.Interface#define}). + *
membersMapMap of members of the mixin.
propertiesMapMap of property definitions. For a description of the format of a property definition see + * {@link qx.core.Property}.
eventsMap + * Map of events the mixin fires. The keys are the names of the events and the values are + * corresponding event type classes. + *
+ * + * @return {qx.Mixin} The configured mixin + */ + define : function(name, config){ + + if(config){ + + // Normalize include + if(config.include && !(qx.Bootstrap.getClass(config.include) === "Array")){ + + config.include = [config.include]; + }; + { + }; + // Create Interface from statics + var mixin = config.statics ? config.statics : { + }; + qx.Bootstrap.setDisplayNames(mixin, name); + for(var key in mixin){ + + if(mixin[key] instanceof Function){ + + mixin[key].$$mixin = mixin; + }; + }; + // Attach configuration + if(config.construct){ + + mixin.$$constructor = config.construct; + qx.Bootstrap.setDisplayName(config.construct, name, "constructor"); + }; + if(config.include){ + + mixin.$$includes = config.include; + }; + if(config.properties){ + + mixin.$$properties = config.properties; + }; + if(config.members){ + + mixin.$$members = config.members; + qx.Bootstrap.setDisplayNames(config.members, name + ".prototype"); + }; + for(var key in mixin.$$members){ + + if(mixin.$$members[key] instanceof Function){ + + mixin.$$members[key].$$mixin = mixin; + }; + }; + if(config.events){ + + mixin.$$events = config.events; + }; + if(config.destruct){ + + mixin.$$destructor = config.destruct; + qx.Bootstrap.setDisplayName(config.destruct, name, "destruct"); + }; + } else { + + var mixin = { + }; + }; + // Add basics + mixin.$$type = "Mixin"; + mixin.name = name; + // Attach toString + mixin.toString = this.genericToString; + // Assign to namespace + mixin.basename = qx.Bootstrap.createNamespace(name, mixin); + // Store class reference in global mixin registry + this.$$registry[name] = mixin; + // Return final mixin + return mixin; + }, + /** + * Check compatibility between mixins (including their includes) + * + * @param mixins {Mixin[]} an array of mixins + * @throws {Error} when there is a conflict between the mixins + * @return {Boolean} true if the mixin passed the compatibilty check + */ + checkCompatibility : function(mixins){ + + var list = this.flatten(mixins); + var len = list.length; + if(len < 2){ + + return true; + }; + var properties = { + }; + var members = { + }; + var events = { + }; + var mixin; + for(var i = 0;i < len;i++){ + + mixin = list[i]; + for(var key in mixin.events){ + + if(events[key]){ + + throw new Error('Conflict between mixin "' + mixin.name + '" and "' + events[key] + '" in member "' + key + '"!'); + }; + events[key] = mixin.name; + }; + for(var key in mixin.properties){ + + if(properties[key]){ + + throw new Error('Conflict between mixin "' + mixin.name + '" and "' + properties[key] + '" in property "' + key + '"!'); + }; + properties[key] = mixin.name; + }; + for(var key in mixin.members){ + + if(members[key]){ + + throw new Error('Conflict between mixin "' + mixin.name + '" and "' + members[key] + '" in member "' + key + '"!'); + }; + members[key] = mixin.name; + }; + }; + return true; + }, + /** + * Checks if a class is compatible to the given mixin (no conflicts) + * + * @param mixin {Mixin} mixin to check + * @param clazz {Class} class to check + * @throws {Error} when the given mixin is incompatible to the class + * @return {Boolean} true if the mixin is compatible to the given class + */ + isCompatible : function(mixin, clazz){ + + var list = qx.util.OOUtil.getMixins(clazz); + list.push(mixin); + return qx.Mixin.checkCompatibility(list); + }, + /** + * Returns a mixin by name + * + * @param name {String} class name to resolve + * @return {Class} the class + */ + getByName : function(name){ + + return this.$$registry[name]; + }, + /** + * Determine if mixin exists + * + * @param name {String} mixin name to check + * @return {Boolean} true if mixin exists + */ + isDefined : function(name){ + + return this.getByName(name) !== undefined; + }, + /** + * Determine the number of mixins which are defined + * + * @return {Number} the number of mixins + */ + getTotalNumber : function(){ + + return qx.Bootstrap.objectGetLength(this.$$registry); + }, + /** + * Generates a list of all mixins given plus all the + * mixins these includes plus... (deep) + * + * @param mixins {Mixin[] ? []} List of mixins + * @return {Array} List of all mixins + */ + flatten : function(mixins){ + + if(!mixins){ + + return []; + }; + // we need to create a copy and not to modify the existing array + var list = mixins.concat(); + for(var i = 0,l = mixins.length;i < l;i++){ + + if(mixins[i].$$includes){ + + list.push.apply(list, this.flatten(mixins[i].$$includes)); + }; + }; + return list; + }, + /* + --------------------------------------------------------------------------- + PRIVATE/INTERNAL API + --------------------------------------------------------------------------- + */ + /** + * This method will be attached to all mixins to return + * a nice identifier for them. + * + * @internal + * @return {String} The mixin identifier + */ + genericToString : function(){ + + return "[Mixin " + this.name + "]"; + }, + /** Registers all defined mixins */ + $$registry : { + }, + /** @type {Map} allowed keys in mixin definition */ + __allowedKeys : null, + /** + * Validates incoming configuration and checks keys and values + * + * @signature function(name, config) + * @param name {String} The name of the class + * @param config {Map} Configuration map + */ + __validateConfig : function(name, config){ + } + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2007-2008 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Fabian Jakobs (fjakobs) + * Andreas Ecker (ecker) + +************************************************************************ */ +/** + * Basis for Aspect Oriented features in qooxdoo. + * + * This class makes it possible to attach functions (aspects) before or + * after each function call of any function defined in {@link qx.Class#define}. + * + * Classes, which define own aspects must add an explicit require to this class + * in the header comment using the following code: + * + *
+ * #require(qx.core.Aspect)
+ * #ignore(auto-require)
+ * 
+ * + * One example for a qooxdoo aspect is profiling ({@link qx.dev.Profile}). + */ +qx.Bootstrap.define("qx.core.Aspect", { + statics : { + /** @type {Array} Registry for all known aspect wishes */ + __registry : [], + /** + * This function is used by {@link qx.Class#define} to wrap all statics, members and + * constructors. + * + * @param fullName {String} Full name of the function including the class name. + * @param fcn {Function} function to wrap. + * @param type {String} Type of the wrapped function. One of "member", "static", + * "constructor", "destructor" or "property". + * + * @return {Function} wrapped function + */ + wrap : function(fullName, fcn, type){ + + var before = []; + var after = []; + var reg = this.__registry; + var entry; + for(var i = 0;i < reg.length;i++){ + + entry = reg[i]; + if((entry.type == null || type == entry.type || entry.type == "*") && (entry.name == null || fullName.match(entry.name))){ + + entry.pos == -1 ? before.push(entry.fcn) : after.push(entry.fcn); + }; + }; + if(before.length === 0 && after.length === 0){ + + return fcn; + }; + var wrapper = function(){ + + for(var i = 0;i < before.length;i++){ + + before[i].call(this, fullName, fcn, type, arguments); + }; + var ret = fcn.apply(this, arguments); + for(var i = 0;i < after.length;i++){ + + after[i].call(this, fullName, fcn, type, arguments, ret); + }; + return ret; + }; + if(type !== "static"){ + + wrapper.self = fcn.self; + wrapper.base = fcn.base; + }; + fcn.wrapper = wrapper; + wrapper.original = fcn; + return wrapper; + }, + /** + * Register a function to be called just before or after each time + * one of the selected functions is called. + * + * @param fcn {Function} Function to be called just before or after any of the + * selected functions is called. If position is "before" the functions + * supports the same signature as {@link qx.dev.Profile#profileBefore}. If + * position is "after" it supports the same signature as + * {@link qx.dev.Profile#profileAfter}. + * @param position {String?"after"} One of "before" or "after". Whether the function + * should be called before or after the wrapped function. + * @param type {String?null} Type of the wrapped function. One of "member", + * "static", "constructor", "destructor", "property" or "*". null + * is handled identical to "*". + * @param name {String|RegExp?null} Each function, with a full name matching + * this pattern (using fullName.match(name)) will be + * wrapped. + */ + addAdvice : function(fcn, position, type, name){ + + this.__registry.push({ + fcn : fcn, + pos : position === "before" ? -1 : 1, + type : type, + name : name + }); + } + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2012 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Martin Wittemann (wittemann) + +************************************************************************ */ +/** + * This class is responsible for the normalization of the native 'String' object. + * It checks if these methods are available and, if not, appends them to + * ensure compatibility in all browsers. + * For usage samples, check out the attached links. + * + * @group (Polyfill) + */ +qx.Bootstrap.define("qx.lang.normalize.String", { + statics : { + /** + * Removes whitespace from both ends of the string. + * + * MDN documentation | + * Annotated ES5 Spec + * + * @return {String} The trimmed string + */ + trim : function(){ + + return this.replace(/^\s+|\s+$/g, ''); + } + }, + defer : function(statics){ + + // trim + if(!qx.core.Environment.get("ecmascript.string.trim")){ + + String.prototype.trim = statics.trim; + }; + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2012 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Martin Wittemann (wittemann) + +************************************************************************ */ +/** + * This class is responsible for the normalization of the native Object. + * It checks if these methods are available and, if not, appends them to + * ensure compatibility in all browsers. + * For usage samples, check out the attached links. + * + * @group (Polyfill) + */ +qx.Bootstrap.define("qx.lang.normalize.Object", { + statics : { + /** + * Get the keys of a map as array as returned by a "for ... in" statement. + * + * MDN documentation | + * Annotated ES5 Spec + * + * @signature function(map) + * @param map {Object} the map + * @return {Array} array of the keys of the map + */ + keys : qx.Bootstrap.keys + }, + defer : function(statics){ + + // keys + if(!qx.core.Environment.get("ecmascript.object.keys")){ + + Object.keys = statics.keys; + }; + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Sebastian Werner (wpbasti) + * Andreas Ecker (ecker) + +************************************************************************ */ +/** + * This class is used to define interfaces (similar to Java interfaces). + * + * See the description of the {@link #define} method how an interface is + * defined. + * + * @require(qx.lang.normalize.Array) + */ +qx.Bootstrap.define("qx.Interface", { + statics : { + /* + --------------------------------------------------------------------------- + PUBLIC API + --------------------------------------------------------------------------- + */ + /** + * Define a new interface. Interface definitions look much like class definitions. + * + * The main difference is that the bodies of functions defined in members + * and statics are called before the original function with the + * same arguments. This can be used to check the passed arguments. If the + * checks fail, an exception should be thrown. It is convenient to use the + * method defined in {@link qx.core.MAssert} to check the arguments. + * + * In the build version the checks are omitted. + * + * For properties only the names are required so the value of the properties + * can be empty maps. + * + * Example: + *
+     * qx.Interface.define("name",
+     * {
+     *   extend: [SuperInterfaces],
+     *
+     *   statics:
+     *   {
+     *     PI : 3.14
+     *   },
+     *
+     *   properties: {"color": {}, "name": {} },
+     *
+     *   members:
+     *   {
+     *     meth1: function() {},
+     *     meth2: function(a, b) { this.assertArgumentsCount(arguments, 2, 2); },
+     *     meth3: function(c) { this.assertInterface(c.constructor, qx.some.Interface); }
+     *   },
+     *
+     *   events :
+     *   {
+     *     keydown : "qx.event.type.KeySequence"
+     *   }
+     * });
+     * 
+ * + * @param name {String} name of the interface + * @param config {Map ? null} Interface definition structure. The configuration map has the following keys: + * + * + * + * + * + * + * + *
NameTypeDescription
extendInterface |
Interface[]
Single interface or array of interfaces this interface inherits from.
membersMapMap of members of the interface.
staticsMap + * Map of statics of the interface. The statics will not get copied into the target class. + * This is the same behaviour as statics in mixins ({@link qx.Mixin#define}). + *
propertiesMapMap of properties and their definitions.
eventsMapMap of event names and the corresponding event class name.
+ * + * @return {qx.Interface} The configured interface + */ + define : function(name, config){ + + if(config){ + + // Normalize include + if(config.extend && !(qx.Bootstrap.getClass(config.extend) === "Array")){ + + config.extend = [config.extend]; + }; + { + }; + // Create interface from statics + var iface = config.statics ? config.statics : { + }; + // Attach configuration + if(config.extend){ + + iface.$$extends = config.extend; + }; + if(config.properties){ + + iface.$$properties = config.properties; + }; + if(config.members){ + + iface.$$members = config.members; + }; + if(config.events){ + + iface.$$events = config.events; + }; + } else { + + // Create empty interface + var iface = { + }; + }; + // Add Basics + iface.$$type = "Interface"; + iface.name = name; + // Attach toString + iface.toString = this.genericToString; + // Assign to namespace + iface.basename = qx.Bootstrap.createNamespace(name, iface); + // Add to registry + qx.Interface.$$registry[name] = iface; + // Return final interface + return iface; + }, + /** + * Returns an interface by name + * + * @param name {String} class name to resolve + * @return {Class} the class + */ + getByName : function(name){ + + return this.$$registry[name]; + }, + /** + * Determine if interface exists + * + * @param name {String} Interface name to check + * @return {Boolean} true if interface exists + */ + isDefined : function(name){ + + return this.getByName(name) !== undefined; + }, + /** + * Determine the number of interfaces which are defined + * + * @return {Number} the number of interfaces + */ + getTotalNumber : function(){ + + return qx.Bootstrap.objectGetLength(this.$$registry); + }, + /** + * Generates a list of all interfaces including their super interfaces + * (resolved recursively) + * + * @param ifaces {Interface[] ? []} List of interfaces to be resolved + * @return {Array} List of all interfaces + */ + flatten : function(ifaces){ + + if(!ifaces){ + + return []; + }; + // we need to create a copy and not to modify the existing array + var list = ifaces.concat(); + for(var i = 0,l = ifaces.length;i < l;i++){ + + if(ifaces[i].$$extends){ + + list.push.apply(list, this.flatten(ifaces[i].$$extends)); + }; + }; + return list; + }, + /** + * Assert members + * + * @param object {qx.core.Object} The object, which contains the methods + * @param clazz {Class} class of the object + * @param iface {Interface} the interface to verify + * @param wrap {Boolean ? false} wrap functions required by interface to + * check parameters etc. + * @param shouldThrow {Boolean} if false, the method + * will return a boolean instead of throwing an exception + * @return {Boolean} true if all members are supported + */ + __checkMembers : function(object, clazz, iface, wrap, shouldThrow){ + + // Validate members + var members = iface.$$members; + if(members){ + + for(var key in members){ + + if(qx.Bootstrap.isFunction(members[key])){ + + var isPropertyMethod = this.__isPropertyMethod(clazz, key); + var hasMemberFunction = isPropertyMethod || qx.Bootstrap.isFunction(object[key]); + if(!hasMemberFunction){ + + if(shouldThrow){ + + throw new Error('Implementation of method "' + key + '" is missing in class "' + clazz.classname + '" required by interface "' + iface.name + '"'); + } else { + + return false; + }; + }; + // Only wrap members if the interface was not been applied yet. This + // can easily be checked by the recursive hasInterface method. + var shouldWrapFunction = wrap === true && !isPropertyMethod && !qx.util.OOUtil.hasInterface(clazz, iface); + if(shouldWrapFunction){ + + object[key] = this.__wrapInterfaceMember(iface, object[key], key, members[key]); + }; + } else { + + // Other members are not checked more detailed because of + // JavaScript's loose type handling + if(typeof object[key] === undefined){ + + if(typeof object[key] !== "function"){ + + if(shouldThrow){ + + throw new Error('Implementation of member "' + key + '" is missing in class "' + clazz.classname + '" required by interface "' + iface.name + '"'); + } else { + + return false; + }; + }; + }; + }; + }; + }; + if(!shouldThrow){ + + return true; + }; + }, + /** + * Internal helper to detect if the method will be generated by the + * property system. + * + * @param clazz {Class} The current class. + * @param methodName {String} The name of the method. + * + * @return {Boolean} true, if the method will be generated by the property + * system. + */ + __isPropertyMethod : function(clazz, methodName){ + + var match = methodName.match(/^(is|toggle|get|set|reset)(.*)$/); + if(!match){ + + return false; + }; + var propertyName = qx.Bootstrap.firstLow(match[2]); + var isPropertyMethod = qx.util.OOUtil.getPropertyDefinition(clazz, propertyName); + if(!isPropertyMethod){ + + return false; + }; + var isBoolean = match[0] == "is" || match[0] == "toggle"; + if(isBoolean){ + + return qx.util.OOUtil.getPropertyDefinition(clazz, propertyName).check == "Boolean"; + }; + return true; + }, + /** + * Assert properties + * + * @param clazz {Class} class to check interface for + * @param iface {Interface} the interface to verify + * @param shouldThrow {Boolean} if false, the method + * will return a boolean instead of throwing an exception + * @return {Boolean} true if all properties are supported + */ + __checkProperties : function(clazz, iface, shouldThrow){ + + if(iface.$$properties){ + + for(var key in iface.$$properties){ + + if(!qx.util.OOUtil.getPropertyDefinition(clazz, key)){ + + if(shouldThrow){ + + throw new Error('The property "' + key + '" is not supported by Class "' + clazz.classname + '"!'); + } else { + + return false; + }; + }; + }; + }; + if(!shouldThrow){ + + return true; + }; + }, + /** + * Assert events + * + * @param clazz {Class} class to check interface for + * @param iface {Interface} the interface to verify + * @param shouldThrow {Boolean} if false, the method + * will return a boolean instead of throwing an exception + * @return {Boolean} true if all events are supported + */ + __checkEvents : function(clazz, iface, shouldThrow){ + + if(iface.$$events){ + + for(var key in iface.$$events){ + + if(!qx.util.OOUtil.supportsEvent(clazz, key)){ + + if(shouldThrow){ + + throw new Error('The event "' + key + '" is not supported by Class "' + clazz.classname + '"!'); + } else { + + return false; + }; + }; + }; + }; + if(!shouldThrow){ + + return true; + }; + }, + /** + * Asserts that the given object implements all the methods defined in the + * interface. This method throws an exception if the object does not + * implement the interface. + * + * @param object {qx.core.Object} Object to check interface for + * @param iface {Interface} The interface to verify + */ + assertObject : function(object, iface){ + + var clazz = object.constructor; + this.__checkMembers(object, clazz, iface, false, true); + this.__checkProperties(clazz, iface, true); + this.__checkEvents(clazz, iface, true); + // Validate extends, recursive + var extend = iface.$$extends; + if(extend){ + + for(var i = 0,l = extend.length;i < l;i++){ + + this.assertObject(object, extend[i]); + }; + }; + }, + /** + * Checks if an interface is implemented by a class + * + * @param clazz {Class} class to check interface for + * @param iface {Interface} the interface to verify + * @param wrap {Boolean ? false} wrap functions required by interface to + * check parameters etc. + */ + assert : function(clazz, iface, wrap){ + + this.__checkMembers(clazz.prototype, clazz, iface, wrap, true); + this.__checkProperties(clazz, iface, true); + this.__checkEvents(clazz, iface, true); + // Validate extends, recursive + var extend = iface.$$extends; + if(extend){ + + for(var i = 0,l = extend.length;i < l;i++){ + + this.assert(clazz, extend[i], wrap); + }; + }; + }, + /** + * Asserts that the given object implements all the methods defined in the + * interface. + * + * @param object {qx.core.Object} Object to check interface for + * @param iface {Interface} The interface to verify + * @return {Boolean} true if the objects implements the interface + */ + objectImplements : function(object, iface){ + + var clazz = object.constructor; + if(!this.__checkMembers(object, clazz, iface) || !this.__checkProperties(clazz, iface) || !this.__checkEvents(clazz, iface)){ + + return false; + }; + // Validate extends, recursive + var extend = iface.$$extends; + if(extend){ + + for(var i = 0,l = extend.length;i < l;i++){ + + if(!this.objectImplements(object, extend[i])){ + + return false; + }; + }; + }; + return true; + }, + /** + * Tests whether an interface is implemented by a class, without throwing an + * exception when it doesn't. + * + * @param clazz {Class} class to check interface for + * @param iface {Interface} the interface to verify + * @return {Boolean} true if interface is implemented + */ + classImplements : function(clazz, iface){ + + if(!this.__checkMembers(clazz.prototype, clazz, iface) || !this.__checkProperties(clazz, iface) || !this.__checkEvents(clazz, iface)){ + + return false; + }; + // Validate extends, recursive + var extend = iface.$$extends; + if(extend){ + + for(var i = 0,l = extend.length;i < l;i++){ + + if(!this.has(clazz, extend[i])){ + + return false; + }; + }; + }; + return true; + }, + /* + --------------------------------------------------------------------------- + PRIVATE/INTERNAL API + --------------------------------------------------------------------------- + */ + /** + * This method will be attached to all interface to return + * a nice identifier for them. + * + * @internal + * @return {String} The interface identifier + */ + genericToString : function(){ + + return "[Interface " + this.name + "]"; + }, + /** Registry of all defined interfaces */ + $$registry : { + }, + /** + * Wrap a method with a precondition check. + * + * @signature function(iface, origFunction, functionName, preCondition) + * @param iface {String} Name of the interface, where the pre condition + * was defined. (Used in error messages). + * @param origFunction {Function} function to wrap. + * @param functionName {String} name of the function. (Used in error messages). + * @param preCondition {Function}. This function gets called with the arguments of the + * original function. If this function return true the original function is called. + * Otherwise an exception is thrown. + * @return {Function} wrapped function + */ + __wrapInterfaceMember : function(iface, origFunction, functionName, preCondition){ + }, + /** @type {Map} allowed keys in interface definition */ + __allowedKeys : null, + /** + * Validates incoming configuration and checks keys and values + * + * @signature function(name, config) + * @param name {String} The name of the class + * @param config {Map} Configuration map + */ + __validateConfig : function(name, config){ + } + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2012 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Martin Wittemann (wittemann) + +************************************************************************ */ +/** + * This class is responsible for the normalization of the native 'Error' object. + * It contains a simple bugfix for toString which might not print out the proper + * error message. + * + * @group (Polyfill) + */ +qx.Bootstrap.define("qx.lang.normalize.Error", { + statics : { + /** + * Returns a string representation of the Error object. + * + * MDN documentation | + * Annotated ES5 Spec + * + * @return {String} Error message + */ + toString : function(){ + + var name = this.name || "Error"; + var message = this.message || ""; + if(name === "" && message === ""){ + + return "Error"; + }; + if(name === ""){ + + return message; + }; + if(message === ""){ + + return name; + }; + return name + ": " + message; + } + }, + defer : function(statics){ + + // toString + if(!qx.core.Environment.get("ecmascript.error.toString")){ + + Error.prototype.toString = statics.toString; + }; + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Sebastian Werner (wpbasti) + * Andreas Ecker (ecker) + * Martin Wittemann (martinwittemann) + +************************************************************************ */ +/** + * Internal class for handling of dynamic properties. Should only be used + * through the methods provided by {@link qx.Class}. + * + * For a complete documentation of properties take a look at + * http://manual.qooxdoo.org/${qxversion}/pages/core.html#properties. + * + * + * *Normal properties* + * + * The properties key in the class definition map of {@link qx.Class#define} + * is used to generate the properties. + * + * Valid keys of a property definition are: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
NameTypeDescription
checkArray, String, Function + * The check is used to check the type the incoming value of a property. This will only + * be executed in the source version. The build version will not contain the checks. + * The check can be: + *
    + *
  • a custom check function. The function takes the incoming value as a parameter and must + * return a boolean value to indicate whether the values is valid. + *
  • + *
  • inline check code as a string e.g. "value > 0 && value < 100"
  • + *
  • a class name e.g. qx.ui.form.Button
  • + *
  • a name of an interface the value must implement
  • + *
  • an array of all valid values
  • + *
  • one of the predefined checks: Boolean, String, Number, Integer, Float, Double, + * Object, Array, Map, Class, Mixin, Interface, Theme, Error, RegExp, Function, + * Date, Node, Element, Document, Window, Event + *
  • + *
      + *
initvar + * Sets the default/initial value of the property. If no property value is set or the property + * gets reset, the getter will return the init value. + *
applyString + * On change of the property value the method of the specified name will be called. The signature of + * the method is function(newValue, oldValue, propertyName). It is conventional to name + * the callback _apply + PropertyName, with the property name camel-cased (e.g. + * "_applyFooBar" for a property fooBar). + *
eventString + * On change of the property value an event with the given name will be dispatched. The event type is + * {@link qx.event.type.Data}. + *
themeableBoolean + * Whether this property can be set using themes. + *
inheritableBoolean + * Whether the property value should be inheritable. If the property does not have an user defined or an + * init value, the property will try to get the value from the parent of the current object. + *
nullableBoolean + * Whether null is an allowed value of the property. This is complementary to the check + * defined using the check key. + *
refineBoolean + * Whether the property definition is a refinement of a property in one of the super classes of the class. + * Only the init value can be changed using refine. + *
transformString + * On setting of the property value the method of the specified name will + * be called. The signature of the method is function(value). + * The parameter value is the value passed to the setter. + * The function must return the modified or unmodified value. + * Transformation occurs before the check function, so both may be + * specified if desired. Alternatively, the transform function may throw + * an error if the value passed to it is invalid. + *
validateFunction, String + * On setting of the property value the method of the specified name will + * be called. The signature of the method is function(value). + * The parameter value is the value passed to the setter. + * If the validation fails, an qx.core.ValidationError should + * be thrown by the validation function. Otherwise, just do nothing in the + * function.
+ * If a string is given, the string should hold a reference to a member + * method.
+ * "methodname" for example + * "__validateProperty"
+ * There are some default validators in the {@link qx.util.Validate} class. + * See this documentation for usage examples. + *
dereferenceBoolean + * By default, the references to the values (current, init, ...) of the + * property will be stored as references on the object. When disposing + * this object, the references will not be deleted. Setting the + * dereference key to true tells the property system to delete all + * connections made by this property on dispose. This can be necessary for + * disconnecting DOM objects to allow the garbage collector to work + * properly. + *
deferredInitBoolean + * Allow for a deferred initialization for reference types. Defaults to false. + *
+ * + * *Property groups* + * + * Property groups are defined in a similar way but support a different set of keys: + * + * + * + * + * + * + *
NameTypeDescription
groupString[] + * A list of property names which should be set using the property group. + *
modeString + * If mode is set to "shorthand", the properties can be set using a CSS like shorthand mode. + *
themeableBoolean + * Whether this property can be set using themes. + *
+ * + * @internal + * @ignore(qx.Interface) + */ +qx.Bootstrap.define("qx.core.Property", { + statics : { + /** + * This is a method which does nothing than gethering dependencies for the + * module system. Calling this method is useless because it does nothing. + */ + __gatherDependency : function(){ + + if(qx.core.Environment.get("module.events")){ + + qx.event.type.Data; + qx.event.dispatch.Direct; + }; + }, + /** + * Built-in checks + * The keys could be used in the check of the properties + */ + __checks : { + "Boolean" : 'qx.core.Assert.assertBoolean(value, msg) || true', + "String" : 'qx.core.Assert.assertString(value, msg) || true', + "Number" : 'qx.core.Assert.assertNumber(value, msg) || true', + "Integer" : 'qx.core.Assert.assertInteger(value, msg) || true', + "PositiveNumber" : 'qx.core.Assert.assertPositiveNumber(value, msg) || true', + "PositiveInteger" : 'qx.core.Assert.assertPositiveInteger(value, msg) || true', + "Error" : 'qx.core.Assert.assertInstance(value, Error, msg) || true', + "RegExp" : 'qx.core.Assert.assertInstance(value, RegExp, msg) || true', + "Object" : 'qx.core.Assert.assertObject(value, msg) || true', + "Array" : 'qx.core.Assert.assertArray(value, msg) || true', + "Map" : 'qx.core.Assert.assertMap(value, msg) || true', + "Function" : 'qx.core.Assert.assertFunction(value, msg) || true', + "Date" : 'qx.core.Assert.assertInstance(value, Date, msg) || true', + "Node" : 'value !== null && value.nodeType !== undefined', + "Element" : 'value !== null && value.nodeType === 1 && value.attributes', + "Document" : 'value !== null && value.nodeType === 9 && value.documentElement', + "Window" : 'value !== null && value.document', + "Event" : 'value !== null && value.type !== undefined', + "Class" : 'value !== null && value.$$type === "Class"', + "Mixin" : 'value !== null && value.$$type === "Mixin"', + "Interface" : 'value !== null && value.$$type === "Interface"', + "Theme" : 'value !== null && value.$$type === "Theme"', + "Color" : 'qx.lang.Type.isString(value) && qx.util.ColorUtil.isValidPropertyValue(value)', + "Decorator" : 'value !== null && qx.theme.manager.Decoration.getInstance().isValidPropertyValue(value)', + "Font" : 'value !== null && qx.theme.manager.Font.getInstance().isDynamic(value)' + }, + /** + * Contains types from {@link #__checks} list which need to be dereferenced + */ + __dereference : { + "Node" : true, + "Element" : true, + "Document" : true, + "Window" : true, + "Event" : true + }, + /** + * Inherit value, used to override defaults etc. to force inheritance + * even if property value is not undefined (through multi-values) + * + * @internal + */ + $$inherit : "inherit", + /** + * Caching field names for each property created + * + * @internal + */ + $$store : { + runtime : { + }, + user : { + }, + theme : { + }, + inherit : { + }, + init : { + }, + useinit : { + } + }, + /** + * Caching function names for each property created + * + * @internal + */ + $$method : { + get : { + }, + set : { + }, + reset : { + }, + init : { + }, + refresh : { + }, + setRuntime : { + }, + resetRuntime : { + }, + setThemed : { + }, + resetThemed : { + } + }, + /** + * Supported keys for property defintions + * + * @internal + */ + $$allowedKeys : { + name : "string", + // String + dereference : "boolean", + // Boolean + inheritable : "boolean", + // Boolean + nullable : "boolean", + // Boolean + themeable : "boolean", + // Boolean + refine : "boolean", + // Boolean + init : null, + // var + apply : "string", + // String + event : "string", + // String + check : null, + // Array, String, Function + transform : "string", + // String + deferredInit : "boolean", + // Boolean + validate : null + }, + /** + * Supported keys for property group definitions + * + * @internal + */ + $$allowedGroupKeys : { + name : "string", + // String + group : "object", + // Array + mode : "string", + // String + themeable : "boolean" + }, + /** Contains names of inheritable properties, filled by {@link qx.Class.define} */ + $$inheritable : { + }, + /** + * Generate optimized refresh method and attach it to the class' prototype + * + * @param clazz {Class} clazz to which the refresher should be added + */ + __executeOptimizedRefresh : function(clazz){ + + var inheritables = this.__getInheritablesOfClass(clazz); + if(!inheritables.length){ + + var refresher = function(){ + }; + } else { + + refresher = this.__createRefresher(inheritables); + }; + clazz.prototype.$$refreshInheritables = refresher; + }, + /** + * Get the names of all inheritable properties of the given class + * + * @param clazz {Class} class to get the inheritable properties of + * @return {String[]} List of property names + */ + __getInheritablesOfClass : function(clazz){ + + var inheritable = []; + while(clazz){ + + var properties = clazz.$$properties; + if(properties){ + + for(var name in this.$$inheritable){ + + // Whether the property is available in this class + // and whether it is inheritable in this class as well + if(properties[name] && properties[name].inheritable){ + + inheritable.push(name); + }; + }; + }; + clazz = clazz.superclass; + }; + return inheritable; + }, + /** + * Assemble the refresher code and return the generated function + * + * @param inheritables {String[]} list of inheritable properties + * @return {Function} refresher function + */ + __createRefresher : function(inheritables){ + + var inherit = this.$$store.inherit; + var init = this.$$store.init; + var refresh = this.$$method.refresh; + var code = ["var parent = this.getLayoutParent();", "if (!parent) return;"]; + for(var i = 0,l = inheritables.length;i < l;i++){ + + var name = inheritables[i]; + code.push("var value = parent.", inherit[name], ";", "if (value===undefined) value = parent.", init[name], ";", "this.", refresh[name], "(value);"); + }; + return new Function(code.join("")); + }, + /** + * Attach $$refreshInheritables method stub to the given class + * + * @param clazz {Class} clazz to which the refresher should be added + */ + attachRefreshInheritables : function(clazz){ + + clazz.prototype.$$refreshInheritables = function(){ + + qx.core.Property.__executeOptimizedRefresh(clazz); + return this.$$refreshInheritables(); + }; + }, + /** + * Attach one property to class + * + * @param clazz {Class} Class to attach properties to + * @param name {String} Name of property + * @param config {Map} Configuration map of property + */ + attachMethods : function(clazz, name, config){ + + // Divide groups from "normal" properties + config.group ? this.__attachGroupMethods(clazz, config, name) : this.__attachPropertyMethods(clazz, config, name); + }, + /** + * Attach group methods + * + * @param clazz {Class} Class to attach properties to + * @param config {Map} Property configuration + * @param name {String} Name of the property + */ + __attachGroupMethods : function(clazz, config, name){ + + var upname = qx.Bootstrap.firstUp(name); + var members = clazz.prototype; + var themeable = config.themeable === true; + { + }; + var setter = []; + var resetter = []; + if(themeable){ + + var styler = []; + var unstyler = []; + }; + var argHandler = "var a=arguments[0] instanceof Array?arguments[0]:arguments;"; + setter.push(argHandler); + if(themeable){ + + styler.push(argHandler); + }; + if(config.mode == "shorthand"){ + + var shorthand = "a=qx.lang.Array.fromShortHand(qx.lang.Array.fromArguments(a));"; + setter.push(shorthand); + if(themeable){ + + styler.push(shorthand); + }; + }; + for(var i = 0,a = config.group,l = a.length;i < l;i++){ + + { + }; + setter.push("this.", this.$$method.set[a[i]], "(a[", i, "]);"); + resetter.push("this.", this.$$method.reset[a[i]], "();"); + if(themeable){ + + { + }; + styler.push("this.", this.$$method.setThemed[a[i]], "(a[", i, "]);"); + unstyler.push("this.", this.$$method.resetThemed[a[i]], "();"); + }; + }; + // Attach setter + this.$$method.set[name] = "set" + upname; + members[this.$$method.set[name]] = new Function(setter.join("")); + // Attach resetter + this.$$method.reset[name] = "reset" + upname; + members[this.$$method.reset[name]] = new Function(resetter.join("")); + if(themeable){ + + // Attach styler + this.$$method.setThemed[name] = "setThemed" + upname; + members[this.$$method.setThemed[name]] = new Function(styler.join("")); + // Attach unstyler + this.$$method.resetThemed[name] = "resetThemed" + upname; + members[this.$$method.resetThemed[name]] = new Function(unstyler.join("")); + }; + }, + /** + * Attach property methods + * + * @param clazz {Class} Class to attach properties to + * @param config {Map} Property configuration + * @param name {String} Name of the property + */ + __attachPropertyMethods : function(clazz, config, name){ + + var upname = qx.Bootstrap.firstUp(name); + var members = clazz.prototype; + { + }; + // Fill dispose value + if(config.dereference === undefined && typeof config.check === "string"){ + + config.dereference = this.__shouldBeDereferenced(config.check); + }; + var method = this.$$method; + var store = this.$$store; + store.runtime[name] = "$$runtime_" + name; + store.user[name] = "$$user_" + name; + store.theme[name] = "$$theme_" + name; + store.init[name] = "$$init_" + name; + store.inherit[name] = "$$inherit_" + name; + store.useinit[name] = "$$useinit_" + name; + method.get[name] = "get" + upname; + members[method.get[name]] = function(){ + + return qx.core.Property.executeOptimizedGetter(this, clazz, name, "get"); + }; + method.set[name] = "set" + upname; + members[method.set[name]] = function(value){ + + return qx.core.Property.executeOptimizedSetter(this, clazz, name, "set", arguments); + }; + method.reset[name] = "reset" + upname; + members[method.reset[name]] = function(){ + + return qx.core.Property.executeOptimizedSetter(this, clazz, name, "reset"); + }; + if(config.inheritable || config.apply || config.event || config.deferredInit){ + + method.init[name] = "init" + upname; + members[method.init[name]] = function(value){ + + return qx.core.Property.executeOptimizedSetter(this, clazz, name, "init", arguments); + }; + { + }; + }; + if(config.inheritable){ + + method.refresh[name] = "refresh" + upname; + members[method.refresh[name]] = function(value){ + + return qx.core.Property.executeOptimizedSetter(this, clazz, name, "refresh", arguments); + }; + { + }; + }; + method.setRuntime[name] = "setRuntime" + upname; + members[method.setRuntime[name]] = function(value){ + + return qx.core.Property.executeOptimizedSetter(this, clazz, name, "setRuntime", arguments); + }; + method.resetRuntime[name] = "resetRuntime" + upname; + members[method.resetRuntime[name]] = function(){ + + return qx.core.Property.executeOptimizedSetter(this, clazz, name, "resetRuntime"); + }; + if(config.themeable){ + + method.setThemed[name] = "setThemed" + upname; + members[method.setThemed[name]] = function(value){ + + return qx.core.Property.executeOptimizedSetter(this, clazz, name, "setThemed", arguments); + }; + method.resetThemed[name] = "resetThemed" + upname; + members[method.resetThemed[name]] = function(){ + + return qx.core.Property.executeOptimizedSetter(this, clazz, name, "resetThemed"); + }; + { + }; + }; + if(config.check === "Boolean"){ + + members["toggle" + upname] = new Function("return this." + method.set[name] + "(!this." + method.get[name] + "())"); + members["is" + upname] = new Function("return this." + method.get[name] + "()"); + { + }; + }; + { + }; + }, + /** + * Returns if the reference for the given property check should be removed + * on dispose. + * + * @param check {var} The check of the property definition. + * @return {Boolean} If the dereference key should be set. + */ + __shouldBeDereferenced : function(check){ + + return !!this.__dereference[check]; + }, + /** @type {Map} Internal data field for error messages used by {@link #error} */ + __errors : { + '0' : 'Could not change or apply init value after constructing phase!', + '1' : 'Requires exactly one argument!', + '2' : 'Undefined value is not allowed!', + '3' : 'Does not allow any arguments!', + '4' : 'Null value is not allowed!', + '5' : 'Is invalid!' + }, + /** + * Error method used by the property system to report errors. + * + * @param obj {qx.core.Object} Any qooxdoo object + * @param id {Integer} Numeric error identifier + * @param property {String} Name of the property + * @param variant {String} Name of the method variant e.g. "set", "reset", ... + * @param value {var} Incoming value + */ + error : function(obj, id, property, variant, value){ + + var classname = obj.constructor.classname; + var msg = "Error in property " + property + " of class " + classname + " in method " + this.$$method[variant][property] + " with incoming value '" + value + "': "; + throw new Error(msg + (this.__errors[id] || "Unknown reason: " + id)); + }, + /** + * Compiles a string builder object to a function, executes the function and + * returns the return value. + * + * @param instance {Object} Instance which have called the original method + * @param members {Object} Prototype members map where the new function should be stored + * @param name {String} Name of the property + * @param variant {String} Function variant e.g. get, set, reset, ... + * @param code {Array} Array which contains the code + * @param args {arguments} Incoming arguments of wrapper method + * @return {var} Return value of the generated function + */ + __unwrapFunctionFromCode : function(instance, members, name, variant, code, args){ + + var store = this.$$method[variant][name]; + { + + members[store] = new Function("value", code.join("")); + }; + // Enable profiling code + if(qx.core.Environment.get("qx.aspects")){ + + members[store] = qx.core.Aspect.wrap(instance.classname + "." + store, members[store], "property"); + }; + qx.Bootstrap.setDisplayName(members[store], instance.classname + ".prototype", store); + // Executing new function + if(args === undefined){ + + return instance[store](); + } else { + + return instance[store](args[0]); + }; + }, + /** + * Generates the optimized getter + * Supported variants: get + * + * @param instance {Object} the instance which calls the method + * @param clazz {Class} the class which originally defined the property + * @param name {String} name of the property + * @param variant {String} Method variant. + * @return {var} Execute return value of apply generated function, generally the incoming value + */ + executeOptimizedGetter : function(instance, clazz, name, variant){ + + var config = clazz.$$properties[name]; + var members = clazz.prototype; + var code = []; + var store = this.$$store; + code.push('if(this.', store.runtime[name], '!==undefined)'); + code.push('return this.', store.runtime[name], ';'); + if(config.inheritable){ + + code.push('else if(this.', store.inherit[name], '!==undefined)'); + code.push('return this.', store.inherit[name], ';'); + code.push('else '); + }; + code.push('if(this.', store.user[name], '!==undefined)'); + code.push('return this.', store.user[name], ';'); + if(config.themeable){ + + code.push('else if(this.', store.theme[name], '!==undefined)'); + code.push('return this.', store.theme[name], ';'); + }; + if(config.deferredInit && config.init === undefined){ + + code.push('else if(this.', store.init[name], '!==undefined)'); + code.push('return this.', store.init[name], ';'); + }; + code.push('else '); + if(config.init !== undefined){ + + if(config.inheritable){ + + code.push('var init=this.', store.init[name], ';'); + if(config.nullable){ + + code.push('if(init==qx.core.Property.$$inherit)init=null;'); + } else if(config.init !== undefined){ + + code.push('return this.', store.init[name], ';'); + } else { + + code.push('if(init==qx.core.Property.$$inherit)throw new Error("Inheritable property ', name, ' of an instance of ', clazz.classname, ' is not (yet) ready!");'); + }; + code.push('return init;'); + } else { + + code.push('return this.', store.init[name], ';'); + }; + } else if(config.inheritable || config.nullable){ + + code.push('return null;'); + } else { + + code.push('throw new Error("Property ', name, ' of an instance of ', clazz.classname, ' is not (yet) ready!");'); + }; + return this.__unwrapFunctionFromCode(instance, members, name, variant, code); + }, + /** + * Generates the optimized setter + * Supported variants: set, reset, init, refresh, style, unstyle + * + * @param instance {Object} the instance which calls the method + * @param clazz {Class} the class which originally defined the property + * @param name {String} name of the property + * @param variant {String} Method variant. + * @param args {arguments} Incoming arguments of wrapper method + * @return {var} Execute return value of apply generated function, generally the incoming value + */ + executeOptimizedSetter : function(instance, clazz, name, variant, args){ + + var config = clazz.$$properties[name]; + var members = clazz.prototype; + var code = []; + var incomingValue = variant === "set" || variant === "setThemed" || variant === "setRuntime" || (variant === "init" && config.init === undefined); + var hasCallback = config.apply || config.event || config.inheritable; + var store = this.__getStore(variant, name); + this.__emitSetterPreConditions(code, config, name, variant, incomingValue); + if(incomingValue){ + + this.__emitIncomingValueTransformation(code, clazz, config, name); + }; + if(hasCallback){ + + this.__emitOldNewComparison(code, incomingValue, store, variant); + }; + if(config.inheritable){ + + code.push('var inherit=prop.$$inherit;'); + }; + { + }; + if(!hasCallback){ + + this.__emitStoreValue(code, name, variant, incomingValue); + } else { + + this.__emitStoreComputedAndOldValue(code, config, name, variant, incomingValue); + }; + if(config.inheritable){ + + this.__emitStoreInheritedPropertyValue(code, config, name, variant); + } else if(hasCallback){ + + this.__emitNormalizeUndefinedValues(code, config, name, variant); + }; + if(hasCallback){ + + this.__emitCallCallback(code, config, name, variant); + // Refresh children + // Requires the parent/children interface + if(config.inheritable && members._getChildren){ + + this.__emitRefreshChildrenValue(code, name); + }; + }; + // Return value + if(incomingValue){ + + code.push('return value;'); + }; + return this.__unwrapFunctionFromCode(instance, members, name, variant, code, args); + }, + /** + * Get the object to store the value for the given variant + * + * @param variant {String} Method variant. + * @param name {String} name of the property + * + * @return {Object} the value store + */ + __getStore : function(variant, name){ + + if(variant === "setRuntime" || variant === "resetRuntime"){ + + var store = this.$$store.runtime[name]; + } else if(variant === "setThemed" || variant === "resetThemed"){ + + store = this.$$store.theme[name]; + } else if(variant === "init"){ + + store = this.$$store.init[name]; + } else { + + store = this.$$store.user[name]; + };; + return store; + }, + /** + * Emit code to check the arguments pre-conditions + * + * @param code {String[]} String array to append the code to + * @param config {Object} The property configuration map + * @param name {String} name of the property + * @param variant {String} Method variant. + * @param incomingValue {Boolean} Whether the setter has an incoming value + */ + __emitSetterPreConditions : function(code, config, name, variant, incomingValue){ + + { + + if(!config.nullable || config.check || config.inheritable){ + + code.push('var prop=qx.core.Property;'); + }; + // Undefined check + if(variant === "set"){ + + code.push('if(value===undefined)prop.error(this,2,"', name, '","', variant, '",value);'); + }; + }; + }, + /** + * Emit code to apply the "validate" and "transform" config keys. + * + * @param code {String[]} String array to append the code to + * @param clazz {Class} the class which originally defined the property + * @param config {Object} The property configuration map + * @param name {String} name of the property + */ + __emitIncomingValueTransformation : function(code, clazz, config, name){ + + // Call user-provided transform method, if one is provided. Transform + // method should either throw an error or return the new value. + if(config.transform){ + + code.push('value=this.', config.transform, '(value);'); + }; + // Call user-provided validate method, if one is provided. Validate + // method should either throw an error or do nothing. + if(config.validate){ + + // if it is a string + if(typeof config.validate === "string"){ + + code.push('this.', config.validate, '(value);'); + } else if(config.validate instanceof Function){ + + code.push(clazz.classname, '.$$properties.', name); + code.push('.validate.call(this, value);'); + }; + }; + }, + /** + * Emit code, which returns if the incoming value equals the current value. + * + * @param code {String[]} String array to append the code to + * @param incomingValue {Boolean} Whether the setter has an incoming value + * @param store {Object} The data store to use for the incoming value + * @param variant {String} Method variant. + */ + __emitOldNewComparison : function(code, incomingValue, store, variant){ + + var resetValue = (variant === "reset" || variant === "resetThemed" || variant === "resetRuntime"); + if(incomingValue){ + + code.push('if(this.', store, '===value)return value;'); + } else if(resetValue){ + + code.push('if(this.', store, '===undefined)return;'); + }; + }, + /** + * Emit code, which performs validation of the incoming value according to + * the "nullable", "check" and "inheritable" config keys. + * + * @signature function(code, config, clazz, name, variant) + * @param code {String[]} String array to append the code to + * @param config {Object} The property configuration map + * @param clazz {Class} the class which originally defined the property + * @param name {String} name of the property + * @param variant {String} Method variant. + */ + __emitIncomingValueValidation : undefined, + /** + * Emit code to store the incoming value + * + * @param code {String[]} String array to append the code to + * @param name {String} name of the property + * @param variant {String} Method variant. + * @param incomingValue {Boolean} Whether the setter has an incoming value + */ + __emitStoreValue : function(code, name, variant, incomingValue){ + + if(variant === "setRuntime"){ + + code.push('this.', this.$$store.runtime[name], '=value;'); + } else if(variant === "resetRuntime"){ + + code.push('if(this.', this.$$store.runtime[name], '!==undefined)'); + code.push('delete this.', this.$$store.runtime[name], ';'); + } else if(variant === "set"){ + + code.push('this.', this.$$store.user[name], '=value;'); + } else if(variant === "reset"){ + + code.push('if(this.', this.$$store.user[name], '!==undefined)'); + code.push('delete this.', this.$$store.user[name], ';'); + } else if(variant === "setThemed"){ + + code.push('this.', this.$$store.theme[name], '=value;'); + } else if(variant === "resetThemed"){ + + code.push('if(this.', this.$$store.theme[name], '!==undefined)'); + code.push('delete this.', this.$$store.theme[name], ';'); + } else if(variant === "init" && incomingValue){ + + code.push('this.', this.$$store.init[name], '=value;'); + };;;;;; + }, + /** + * Emit code to store the incoming value and compute the "old" and "computed" + * values. + * + * @param code {String[]} String array to append the code to + * @param config {Object} The property configuration map + * @param name {String} name of the property + * @param variant {String} Method variant. + * @param incomingValue {Boolean} Whether the setter has an incoming value + */ + __emitStoreComputedAndOldValue : function(code, config, name, variant, incomingValue){ + + if(config.inheritable){ + + code.push('var computed, old=this.', this.$$store.inherit[name], ';'); + } else { + + code.push('var computed, old;'); + }; + // OLD = RUNTIME VALUE + code.push('if(this.', this.$$store.runtime[name], '!==undefined){'); + if(variant === "setRuntime"){ + + // Replace it with new value + code.push('computed=this.', this.$$store.runtime[name], '=value;'); + } else if(variant === "resetRuntime"){ + + // Delete field + code.push('delete this.', this.$$store.runtime[name], ';'); + // Complex compution of new value + code.push('if(this.', this.$$store.user[name], '!==undefined)'); + code.push('computed=this.', this.$$store.user[name], ';'); + code.push('else if(this.', this.$$store.theme[name], '!==undefined)'); + code.push('computed=this.', this.$$store.theme[name], ';'); + code.push('else if(this.', this.$$store.init[name], '!==undefined){'); + code.push('computed=this.', this.$$store.init[name], ';'); + code.push('this.', this.$$store.useinit[name], '=true;'); + code.push('}'); + } else { + + // Use runtime value as it has higher priority + code.push('old=computed=this.', this.$$store.runtime[name], ';'); + // Store incoming value + if(variant === "set"){ + + code.push('this.', this.$$store.user[name], '=value;'); + } else if(variant === "reset"){ + + code.push('delete this.', this.$$store.user[name], ';'); + } else if(variant === "setThemed"){ + + code.push('this.', this.$$store.theme[name], '=value;'); + } else if(variant === "resetThemed"){ + + code.push('delete this.', this.$$store.theme[name], ';'); + } else if(variant === "init" && incomingValue){ + + code.push('this.', this.$$store.init[name], '=value;'); + };;;; + }; + code.push('}'); + // OLD = USER VALUE + code.push('else if(this.', this.$$store.user[name], '!==undefined){'); + if(variant === "set"){ + + if(!config.inheritable){ + + // Remember old value + code.push('old=this.', this.$$store.user[name], ';'); + }; + // Replace it with new value + code.push('computed=this.', this.$$store.user[name], '=value;'); + } else if(variant === "reset"){ + + if(!config.inheritable){ + + // Remember old value + code.push('old=this.', this.$$store.user[name], ';'); + }; + // Delete field + code.push('delete this.', this.$$store.user[name], ';'); + // Complex compution of new value + code.push('if(this.', this.$$store.runtime[name], '!==undefined)'); + code.push('computed=this.', this.$$store.runtime[name], ';'); + code.push('if(this.', this.$$store.theme[name], '!==undefined)'); + code.push('computed=this.', this.$$store.theme[name], ';'); + code.push('else if(this.', this.$$store.init[name], '!==undefined){'); + code.push('computed=this.', this.$$store.init[name], ';'); + code.push('this.', this.$$store.useinit[name], '=true;'); + code.push('}'); + } else { + + if(variant === "setRuntime"){ + + // Use runtime value where it has higher priority + code.push('computed=this.', this.$$store.runtime[name], '=value;'); + } else if(config.inheritable){ + + // Use user value where it has higher priority + code.push('computed=this.', this.$$store.user[name], ';'); + } else { + + // Use user value where it has higher priority + code.push('old=computed=this.', this.$$store.user[name], ';'); + }; + // Store incoming value + if(variant === "setThemed"){ + + code.push('this.', this.$$store.theme[name], '=value;'); + } else if(variant === "resetThemed"){ + + code.push('delete this.', this.$$store.theme[name], ';'); + } else if(variant === "init" && incomingValue){ + + code.push('this.', this.$$store.init[name], '=value;'); + };; + }; + code.push('}'); + // OLD = THEMED VALUE + if(config.themeable){ + + code.push('else if(this.', this.$$store.theme[name], '!==undefined){'); + if(!config.inheritable){ + + code.push('old=this.', this.$$store.theme[name], ';'); + }; + if(variant === "setRuntime"){ + + code.push('computed=this.', this.$$store.runtime[name], '=value;'); + } else if(variant === "set"){ + + code.push('computed=this.', this.$$store.user[name], '=value;'); + } else if(variant === "setThemed"){ + + code.push('computed=this.', this.$$store.theme[name], '=value;'); + } else if(variant === "resetThemed"){ + + // Delete entry + code.push('delete this.', this.$$store.theme[name], ';'); + // Fallback to init value + code.push('if(this.', this.$$store.init[name], '!==undefined){'); + code.push('computed=this.', this.$$store.init[name], ';'); + code.push('this.', this.$$store.useinit[name], '=true;'); + code.push('}'); + } else if(variant === "init"){ + + if(incomingValue){ + + code.push('this.', this.$$store.init[name], '=value;'); + }; + code.push('computed=this.', this.$$store.theme[name], ';'); + } else if(variant === "refresh"){ + + code.push('computed=this.', this.$$store.theme[name], ';'); + };;;;; + code.push('}'); + }; + // OLD = INIT VALUE + code.push('else if(this.', this.$$store.useinit[name], '){'); + if(!config.inheritable){ + + code.push('old=this.', this.$$store.init[name], ';'); + }; + if(variant === "init"){ + + if(incomingValue){ + + code.push('computed=this.', this.$$store.init[name], '=value;'); + } else { + + code.push('computed=this.', this.$$store.init[name], ';'); + }; + } else if(variant === "set" || variant === "setRuntime" || variant === "setThemed" || variant === "refresh"){ + + code.push('delete this.', this.$$store.useinit[name], ';'); + if(variant === "setRuntime"){ + + code.push('computed=this.', this.$$store.runtime[name], '=value;'); + } else if(variant === "set"){ + + code.push('computed=this.', this.$$store.user[name], '=value;'); + } else if(variant === "setThemed"){ + + code.push('computed=this.', this.$$store.theme[name], '=value;'); + } else if(variant === "refresh"){ + + code.push('computed=this.', this.$$store.init[name], ';'); + };;; + }; + code.push('}'); + // OLD = NONE + // reset(), resetRuntime() and resetStyle() are impossible because otherwise there + // is already an old value + if(variant === "set" || variant === "setRuntime" || variant === "setThemed" || variant === "init"){ + + code.push('else{'); + if(variant === "setRuntime"){ + + code.push('computed=this.', this.$$store.runtime[name], '=value;'); + } else if(variant === "set"){ + + code.push('computed=this.', this.$$store.user[name], '=value;'); + } else if(variant === "setThemed"){ + + code.push('computed=this.', this.$$store.theme[name], '=value;'); + } else if(variant === "init"){ + + if(incomingValue){ + + code.push('computed=this.', this.$$store.init[name], '=value;'); + } else { + + code.push('computed=this.', this.$$store.init[name], ';'); + }; + code.push('this.', this.$$store.useinit[name], '=true;'); + };;; + // refresh() will work with the undefined value, later + code.push('}'); + }; + }, + /** + * Emit code to store the value of an inheritable property + * + * @param code {String[]} String array to append the code to + * @param config {Object} The property configuration map + * @param name {String} name of the property + * @param variant {String} Method variant. + */ + __emitStoreInheritedPropertyValue : function(code, config, name, variant){ + + code.push('if(computed===undefined||computed===inherit){'); + if(variant === "refresh"){ + + code.push('computed=value;'); + } else { + + code.push('var pa=this.getLayoutParent();if(pa)computed=pa.', this.$$store.inherit[name], ';'); + }; + // Fallback to init value if inheritance was unsuccessful + code.push('if((computed===undefined||computed===inherit)&&'); + code.push('this.', this.$$store.init[name], '!==undefined&&'); + code.push('this.', this.$$store.init[name], '!==inherit){'); + code.push('computed=this.', this.$$store.init[name], ';'); + code.push('this.', this.$$store.useinit[name], '=true;'); + code.push('}else{'); + code.push('delete this.', this.$$store.useinit[name], ';}'); + code.push('}'); + // Compare old/new computed value + code.push('if(old===computed)return value;'); + // Note: At this point computed can be "inherit" or "undefined". + // Normalize "inherit" to undefined and delete inherited value + code.push('if(computed===inherit){'); + code.push('computed=undefined;delete this.', this.$$store.inherit[name], ';'); + code.push('}'); + // Only delete inherited value + code.push('else if(computed===undefined)'); + code.push('delete this.', this.$$store.inherit[name], ';'); + // Store inherited value + code.push('else this.', this.$$store.inherit[name], '=computed;'); + // Protect against normalization + code.push('var backup=computed;'); + // After storage finally normalize computed and old value + if(config.init !== undefined && variant !== "init"){ + + code.push('if(old===undefined)old=this.', this.$$store.init[name], ";"); + } else { + + code.push('if(old===undefined)old=null;'); + }; + code.push('if(computed===undefined||computed==inherit)computed=null;'); + }, + /** + * Emit code to normalize the old and incoming values from undefined to + * null. + * + * @param code {String[]} String array to append the code to + * @param config {Object} The property configuration map + * @param name {String} name of the property + * @param variant {String} Method variant. + */ + __emitNormalizeUndefinedValues : function(code, config, name, variant){ + + // Properties which are not inheritable have no possibility to get + // undefined at this position. (Hint: set(), setRuntime() and setThemed() only allow non undefined values) + if(variant !== "set" && variant !== "setRuntime" && variant !== "setThemed"){ + + code.push('if(computed===undefined)computed=null;'); + }; + // Compare old/new computed value + code.push('if(old===computed)return value;'); + // Normalize old value + if(config.init !== undefined && variant !== "init"){ + + code.push('if(old===undefined)old=this.', this.$$store.init[name], ";"); + } else { + + code.push('if(old===undefined)old=null;'); + }; + }, + /** + * Emit code to call the apply method and fire the change event + * + * @param code {String[]} String array to append the code to + * @param config {Object} The property configuration map + * @param name {String} name of the property + * @param variant {String} variant of the method e.g. setThemed + */ + __emitCallCallback : function(code, config, name, variant){ + + // Execute user configured setter + if(config.apply){ + + code.push('this.', config.apply, '(computed, old, "', name, '", "', variant, '");'); + }; + // Fire event + if(config.event){ + + code.push("var reg=qx.event.Registration;", "if(reg.hasListener(this, '", config.event, "')){", "reg.fireEvent(this, '", config.event, "', qx.event.type.Data, [computed, old]", ")}"); + }; + }, + /** + * Emit code to update the inherited values of child objects + * + * @param code {String[]} String array to append the code to + * @param name {String} name of the property + */ + __emitRefreshChildrenValue : function(code, name){ + + code.push('var a=this._getChildren();if(a)for(var i=0,l=a.length;i + * classnameThe fully-qualified name of the class (e.g. "qx.ui.core.Widget"). + * basenameThe namespace part of the class name (e.g. "qx.ui.core"). + * constructorA reference to the constructor of the class. + * superclassA reference to the constructor of the super class. + * + * + * Each method may access static members of the same class by using + * this.self(arguments) ({@link qx.core.Object#self}): + *
+ * statics : { FOO : "bar" },
+ * members: {
+ *   baz: function(x) {
+ *     this.self(arguments).FOO;
+ *     ...
+ *   }
+ * }
+ * 
+ * + * Each overriding method may call the overridden method by using + * this.base(arguments [, ...]) ({@link qx.core.Object#base}). This is also true for calling + * the constructor of the superclass. + *
+ * members: {
+ *   foo: function(x) {
+ *     this.base(arguments, x);
+ *     ...
+ *   }
+ * }
+ * 
+ * + * By using qx.Class within an app, the native JS data types are + * conveniently polyfilled according to {@link qx.lang.normalize}. + * + * @require(qx.Interface) + * @require(qx.Mixin) + * @require(qx.lang.normalize.Array) + * @require(qx.lang.normalize.Date) + * @require(qx.lang.normalize.Error) + * @require(qx.lang.normalize.Function) + * @require(qx.lang.normalize.String) + * @require(qx.lang.normalize.Object) + */ +qx.Bootstrap.define("qx.Class", { + statics : { + /** + * A static reference to the property implementation in the case it + * should be included. + */ + __Property : qx.core.Environment.get("module.property") ? qx.core.Property : null, + /* + --------------------------------------------------------------------------- + PUBLIC METHODS + --------------------------------------------------------------------------- + */ + /** + * Define a new class using the qooxdoo class system. This sets up the + * namespace for the class and generates the class from the definition map. + * + * Example: + *
+     * qx.Class.define("name",
+     * {
+     *   extend : Object, // superclass
+     *   implement : [Interfaces],
+     *   include : [Mixins],
+     *
+     *   statics:
+     *   {
+     *     CONSTANT : 3.141,
+     *
+     *     publicMethod: function() {},
+     *     _protectedMethod: function() {},
+     *     __privateMethod: function() {}
+     *   },
+     *
+     *   properties:
+     *   {
+     *     "tabIndex": { check: "Number", init : -1 }
+     *   },
+     *
+     *   members:
+     *   {
+     *     publicField: "foo",
+     *     publicMethod: function() {},
+     *
+     *     _protectedField: "bar",
+     *     _protectedMethod: function() {},
+     *
+     *     __privateField: "baz",
+     *     __privateMethod: function() {}
+     *   }
+     * });
+     * 
+ * + * @param name {String?null} Name of the class. If null, the class + * will not be added to any namespace which could be handy for testing. + * @param config {Map ? null} Class definition structure. The configuration map has the following keys: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
NameTypeDescription
typeString + * Type of the class. Valid types are "abstract", "static" and "singleton". + * If unset it defaults to a regular non-static class. + *
extendClassThe super class the current class inherits from.
implementInterface | Interface[]Single interface or array of interfaces the class implements.
includeMixin | Mixin[]Single mixin or array of mixins, which will be merged into the class.
constructFunctionThe constructor of the class.
staticsMapMap of static members of the class.
propertiesMapMap of property definitions. For a description of the format of a property definition see + * {@link qx.core.Property}.
membersMapMap of instance members of the class.
environmentMapMap of environment settings for this class. For a description of the format of a setting see + * {@link qx.core.Environment}.
eventsMap + * Map of events the class fires. The keys are the names of the events and the values are the + * corresponding event type class names. + *
deferFunctionFunction that is called at the end of processing the class declaration. It allows access to the declared statics, members and properties.
destructFunctionThe destructor of the class.
+ * @return {Class} The defined class + */ + define : function(name, config){ + + if(!config){ + + config = { + }; + }; + // Normalize include to array + if(config.include && !(qx.Bootstrap.getClass(config.include) === "Array")){ + + config.include = [config.include]; + }; + // Normalize implement to array + if(config.implement && !(qx.Bootstrap.getClass(config.implement) === "Array")){ + + config.implement = [config.implement]; + }; + // Normalize type + var implicitType = false; + if(!config.hasOwnProperty("extend") && !config.type){ + + config.type = "static"; + implicitType = true; + }; + { + }; + // Create the class + var clazz = this.__createClass(name, config.type, config.extend, config.statics, config.construct, config.destruct, config.include); + // Members, properties, events and mixins are only allowed for non-static classes + if(config.extend){ + + // Attach properties + if(config.properties){ + + this.__addProperties(clazz, config.properties, true); + }; + // Attach members + if(config.members){ + + this.__addMembers(clazz, config.members, true, true, false); + }; + // Process events + if(config.events){ + + this.__addEvents(clazz, config.events, true); + }; + // Include mixins + // Must be the last here to detect conflicts + if(config.include){ + + for(var i = 0,l = config.include.length;i < l;i++){ + + this.__addMixin(clazz, config.include[i], false); + }; + }; + } else if(config.hasOwnProperty('extend') && false){ + + throw new Error('"extend" parameter is null or undefined'); + }; + // Process environment + if(config.environment){ + + for(var key in config.environment){ + + qx.core.Environment.add(key, config.environment[key]); + }; + }; + // Interface support for non-static classes + if(config.implement){ + + for(var i = 0,l = config.implement.length;i < l;i++){ + + this.__addInterface(clazz, config.implement[i]); + }; + }; + { + }; + // Process defer + if(config.defer){ + + config.defer.self = clazz; + config.defer(clazz, clazz.prototype, { + add : function(name, config){ + + // build pseudo properties map + var properties = { + }; + properties[name] = config; + // execute generic property handler + qx.Class.__addProperties(clazz, properties, true); + } + }); + }; + return clazz; + }, + /** + * Removes a class from qooxdoo defined by {@link #define} + * + * @param name {String} Name of the class + */ + undefine : function(name){ + + // first, delete the class from the registry + delete this.$$registry[name]; + // delete the class reference from the namespaces and all empty namespaces + var ns = name.split("."); + // build up an array containing all namespace objects including window + var objects = [window]; + for(var i = 0;i < ns.length;i++){ + + objects.push(objects[i][ns[i]]); + }; + // go through all objects and check for the constructor or empty namespaces + for(var i = objects.length - 1;i >= 1;i--){ + + var last = objects[i]; + var parent = objects[i - 1]; + if(qx.Bootstrap.isFunction(last) || qx.Bootstrap.objectGetLength(last) === 0){ + + delete parent[ns[i - 1]]; + } else { + + break; + }; + }; + }, + /** + * Whether the given class exists + * + * @signature function(name) + * @param name {String} class name to check + * @return {Boolean} true if class exists + */ + isDefined : qx.util.OOUtil.classIsDefined, + /** + * Determine the total number of classes + * + * @return {Number} the total number of classes + */ + getTotalNumber : function(){ + + return qx.Bootstrap.objectGetLength(this.$$registry); + }, + /** + * Find a class by its name + * + * @signature function(name) + * @param name {String} class name to resolve + * @return {Class} the class + */ + getByName : qx.Bootstrap.getByName, + /** + * Include all features of the given mixin into the class. The mixin must + * not include any methods or properties that are already available in the + * class. This would only be possible using the {@link #patch} method. + * + * @param clazz {Class} An existing class which should be augmented by including a mixin. + * @param mixin {Mixin} The mixin to be included. + */ + include : function(clazz, mixin){ + + { + }; + qx.Class.__addMixin(clazz, mixin, false); + }, + /** + * Include all features of the given mixin into the class. The mixin may + * include features, which are already defined in the target class. Existing + * features of equal name will be overwritten. + * Please keep in mind that this functionality is not intended for regular + * use, but as a formalized way (and a last resort) in order to patch + * existing classes. + * + * WARNING: You may break working classes and features. + * + * @param clazz {Class} An existing class which should be modified by including a mixin. + * @param mixin {Mixin} The mixin to be included. + */ + patch : function(clazz, mixin){ + + { + }; + qx.Class.__addMixin(clazz, mixin, true); + }, + /** + * Whether a class is a direct or indirect sub class of another class, + * or both classes coincide. + * + * @param clazz {Class} the class to check. + * @param superClass {Class} the potential super class + * @return {Boolean} whether clazz is a sub class of superClass. + */ + isSubClassOf : function(clazz, superClass){ + + if(!clazz){ + + return false; + }; + if(clazz == superClass){ + + return true; + }; + if(clazz.prototype instanceof superClass){ + + return true; + }; + return false; + }, + /** + * Returns the definition of the given property. Returns null + * if the property does not exist. + * + * @signature function(clazz, name) + * @param clazz {Class} class to check + * @param name {String} name of the class to check for + * @return {Map|null} whether the object support the given event. + */ + getPropertyDefinition : qx.util.OOUtil.getPropertyDefinition, + /** + * Returns a list of all properties supported by the given class + * + * @param clazz {Class} Class to query + * @return {String[]} List of all property names + */ + getProperties : function(clazz){ + + var list = []; + while(clazz){ + + if(clazz.$$properties){ + + list.push.apply(list, Object.keys(clazz.$$properties)); + }; + clazz = clazz.superclass; + }; + return list; + }, + /** + * Returns the class or one of its superclasses which contains the + * declaration for the given property in its class definition. Returns null + * if the property is not specified anywhere. + * + * @param clazz {Class} class to look for the property + * @param name {String} name of the property + * @return {Class | null} The class which includes the property + */ + getByProperty : function(clazz, name){ + + while(clazz){ + + if(clazz.$$properties && clazz.$$properties[name]){ + + return clazz; + }; + clazz = clazz.superclass; + }; + return null; + }, + /** + * Whether a class has the given property + * + * @signature function(clazz, name) + * @param clazz {Class} class to check + * @param name {String} name of the property to check for + * @return {Boolean} whether the class includes the given property. + */ + hasProperty : qx.util.OOUtil.hasProperty, + /** + * Returns the event type of the given event. Returns null if + * the event does not exist. + * + * @signature function(clazz, name) + * @param clazz {Class} class to check + * @param name {String} name of the event + * @return {String|null} Event type of the given event. + */ + getEventType : qx.util.OOUtil.getEventType, + /** + * Whether a class supports the given event type + * + * @signature function(clazz, name) + * @param clazz {Class} class to check + * @param name {String} name of the event to check for + * @return {Boolean} whether the class supports the given event. + */ + supportsEvent : qx.util.OOUtil.supportsEvent, + /** + * Whether a class directly includes a mixin. + * + * @param clazz {Class} class to check + * @param mixin {Mixin} the mixin to check for + * @return {Boolean} whether the class includes the mixin directly. + */ + hasOwnMixin : function(clazz, mixin){ + + return clazz.$$includes && clazz.$$includes.indexOf(mixin) !== -1; + }, + /** + * Returns the class or one of its superclasses which contains the + * declaration for the given mixin. Returns null if the mixin is not + * specified anywhere. + * + * @param clazz {Class} class to look for the mixin + * @param mixin {Mixin} mixin to look for + * @return {Class | null} The class which directly includes the given mixin + */ + getByMixin : function(clazz, mixin){ + + var list,i,l; + while(clazz){ + + if(clazz.$$includes){ + + list = clazz.$$flatIncludes; + for(i = 0,l = list.length;i < l;i++){ + + if(list[i] === mixin){ + + return clazz; + }; + }; + }; + clazz = clazz.superclass; + }; + return null; + }, + /** + * Returns a list of all mixins available in a given class. + * + * @signature function(clazz) + * @param clazz {Class} class which should be inspected + * @return {Mixin[]} array of mixins this class uses + */ + getMixins : qx.util.OOUtil.getMixins, + /** + * Whether a given class or any of its superclasses includes a given mixin. + * + * @param clazz {Class} class to check + * @param mixin {Mixin} the mixin to check for + * @return {Boolean} whether the class includes the mixin. + */ + hasMixin : function(clazz, mixin){ + + return !!this.getByMixin(clazz, mixin); + }, + /** + * Whether a given class directly includes an interface. + * + * This function will only return "true" if the interface was defined + * in the class declaration ({@link qx.Class#define}) using the "implement" + * key. + * + * @param clazz {Class} class or instance to check + * @param iface {Interface} the interface to check for + * @return {Boolean} whether the class includes the mixin directly. + */ + hasOwnInterface : function(clazz, iface){ + + return clazz.$$implements && clazz.$$implements.indexOf(iface) !== -1; + }, + /** + * Returns the class or one of its super classes which contains the + * declaration of the given interface. Returns null if the interface is not + * specified anywhere. + * + * @signature function(clazz, iface) + * @param clazz {Class} class to look for the interface + * @param iface {Interface} interface to look for + * @return {Class | null} the class which directly implements the given interface + */ + getByInterface : qx.util.OOUtil.getByInterface, + /** + * Returns a list of all interfaces a given class has to implement. + * + * @param clazz {Class} class which should be inspected + * @return {Interface[]} array of interfaces this class implements + */ + getInterfaces : function(clazz){ + + var list = []; + while(clazz){ + + if(clazz.$$implements){ + + list.push.apply(list, clazz.$$flatImplements); + }; + clazz = clazz.superclass; + }; + return list; + }, + /** + * Whether a given class or any of its super classes includes a given interface. + * + * This function will return "true" if the interface was defined + * in the class declaration ({@link qx.Class#define}) of the class + * or any of its super classes using the "implement" + * key. + * + * @signature function(clazz, iface) + * @param clazz {Class} class to check + * @param iface {Interface} the interface to check for + * @return {Boolean} whether the class includes the interface. + */ + hasInterface : qx.util.OOUtil.hasInterface, + /** + * Whether a given class complies to an interface. + * + * Checks whether all methods defined in the interface are + * implemented. The class does not need to implement + * the interface explicitly in the extend key. + * + * @param obj {Object} class to check + * @param iface {Interface} the interface to check for + * @return {Boolean} whether the class conforms to the interface. + */ + implementsInterface : function(obj, iface){ + + var clazz = obj.constructor; + if(this.hasInterface(clazz, iface)){ + + return true; + }; + if(qx.Interface.objectImplements(obj, iface)){ + + return true; + }; + if(qx.Interface.classImplements(clazz, iface)){ + + return true; + }; + return false; + }, + /** + * Helper method to handle singletons + * + * @internal + * @return {Object} The singleton instance + */ + getInstance : function(){ + + if(!this.$$instance){ + + this.$$allowconstruct = true; + this.$$instance = new this(); + delete this.$$allowconstruct; + }; + return this.$$instance; + }, + /* + --------------------------------------------------------------------------- + PRIVATE/INTERNAL BASICS + --------------------------------------------------------------------------- + */ + /** + * This method will be attached to all classes to return + * a nice identifier for them. + * + * @internal + * @return {String} The class identifier + */ + genericToString : function(){ + + return "[Class " + this.classname + "]"; + }, + /** Stores all defined classes */ + $$registry : qx.Bootstrap.$$registry, + /** @type {Map} allowed keys in non-static class definition */ + __allowedKeys : null, + /** @type {Map} allowed keys in static class definition */ + __staticAllowedKeys : null, + /** + * Validates an incoming configuration and checks for proper keys and values + * + * @signature function(name, config) + * @param name {String} The name of the class + * @param config {Map} Configuration map + */ + __validateConfig : function(name, config){ + }, + /** + * Validates the interfaces required by abstract base classes + * + * @signature function(clazz) + * @param clazz {Class} The configured class. + */ + __validateAbstractInterfaces : function(clazz){ + }, + /** + * Creates a class by type. Supports modern inheritance etc. + * + * @param name {String} Full name of the class + * @param type {String} type of the class, i.e. "static", "abstract" or "singleton" + * @param extend {Class} Superclass to inherit from + * @param statics {Map} Static methods or fields + * @param construct {Function} Constructor of the class + * @param destruct {Function} Destructor of the class + * @param mixins {Mixin[]} array of mixins of the class + * @return {Class} The generated class + */ + __createClass : function(name, type, extend, statics, construct, destruct, mixins){ + + var clazz; + if(!extend && qx.core.Environment.get("qx.aspects") == false){ + + // Create empty/non-empty class + clazz = statics || { + }; + qx.Bootstrap.setDisplayNames(clazz, name); + } else { + + clazz = { + }; + if(extend){ + + // Create default constructor + if(!construct){ + + construct = this.__createDefaultConstructor(); + }; + if(this.__needsConstructorWrapper(extend, mixins)){ + + clazz = this.__wrapConstructor(construct, name, type); + } else { + + clazz = construct; + }; + // Add singleton getInstance() + if(type === "singleton"){ + + clazz.getInstance = this.getInstance; + }; + qx.Bootstrap.setDisplayName(construct, name, "constructor"); + }; + // Copy statics + if(statics){ + + qx.Bootstrap.setDisplayNames(statics, name); + var key; + for(var i = 0,a = Object.keys(statics),l = a.length;i < l;i++){ + + key = a[i]; + var staticValue = statics[key]; + if(qx.core.Environment.get("qx.aspects")){ + + if(staticValue instanceof Function){ + + staticValue = qx.core.Aspect.wrap(name + "." + key, staticValue, "static"); + }; + clazz[key] = staticValue; + } else { + + clazz[key] = staticValue; + }; + }; + }; + }; + // Create namespace + var basename = name ? qx.Bootstrap.createNamespace(name, clazz) : ""; + // Store names in constructor/object + clazz.name = clazz.classname = name; + clazz.basename = basename; + // Store type info + clazz.$$type = "Class"; + if(type){ + + clazz.$$classtype = type; + }; + // Attach toString + if(!clazz.hasOwnProperty("toString")){ + + clazz.toString = this.genericToString; + }; + if(extend){ + + qx.Bootstrap.extendClass(clazz, construct, extend, name, basename); + // Store destruct onto class + if(destruct){ + + if(qx.core.Environment.get("qx.aspects")){ + + destruct = qx.core.Aspect.wrap(name, destruct, "destructor"); + }; + clazz.$$destructor = destruct; + qx.Bootstrap.setDisplayName(destruct, name, "destruct"); + }; + }; + // Store class reference in global class registry + this.$$registry[name] = clazz; + // Return final class object + return clazz; + }, + /* + --------------------------------------------------------------------------- + PRIVATE ADD HELPERS + --------------------------------------------------------------------------- + */ + /** + * Attach events to the class + * + * @param clazz {Class} class to add the events to + * @param events {Map} map of event names the class fires. + * @param patch {Boolean ? false} Enable redefinition of event type? + */ + __addEvents : function(clazz, events, patch){ + + { + + var key,key; + }; + if(clazz.$$events){ + + for(var key in events){ + + clazz.$$events[key] = events[key]; + }; + } else { + + clazz.$$events = events; + }; + }, + /** + * Attach properties to classes + * + * @param clazz {Class} class to add the properties to + * @param properties {Map} map of properties + * @param patch {Boolean ? false} Overwrite property with the limitations of a property + which means you are able to refine but not to replace (esp. for new properties) + */ + __addProperties : function(clazz, properties, patch){ + + // check for the property module + if(!qx.core.Environment.get("module.property")){ + + throw new Error("Property module disabled."); + }; + var config; + if(patch === undefined){ + + patch = false; + }; + var proto = clazz.prototype; + for(var name in properties){ + + config = properties[name]; + { + }; + // Store name into configuration + config.name = name; + // Add config to local registry + if(!config.refine){ + + if(clazz.$$properties === undefined){ + + clazz.$$properties = { + }; + }; + clazz.$$properties[name] = config; + }; + // Store init value to prototype. This makes it possible to + // overwrite this value in derived classes. + if(config.init !== undefined){ + + clazz.prototype["$$init_" + name] = config.init; + }; + // register event name + if(config.event !== undefined){ + + // break if no events layer loaded + if(!qx.core.Environment.get("module.events")){ + + throw new Error("Events module not enabled."); + }; + var event = { + }; + event[config.event] = "qx.event.type.Data"; + this.__addEvents(clazz, event, patch); + }; + // Remember inheritable properties + if(config.inheritable){ + + this.__Property.$$inheritable[name] = true; + if(!proto.$$refreshInheritables){ + + this.__Property.attachRefreshInheritables(clazz); + }; + }; + if(!config.refine){ + + this.__Property.attachMethods(clazz, name, config); + }; + }; + }, + /** + * Validates the given property + * + * @signature function(clazz, name, config, patch) + * @param clazz {Class} class to add property to + * @param name {String} name of the property + * @param config {Map} configuration map + * @param patch {Boolean ? false} enable refine/patch? + */ + __validateProperty : null, + /** + * Attach members to a class + * + * @param clazz {Class} clazz to add members to + * @param members {Map} The map of members to attach + * @param patch {Boolean ? false} Enable patching of + * @param base {Boolean ? true} Attach base flag to mark function as members + * of this class + * @param wrap {Boolean ? false} Whether the member method should be wrapped. + * this is needed to allow base calls in patched mixin members. + */ + __addMembers : function(clazz, members, patch, base, wrap){ + + var proto = clazz.prototype; + var key,member; + qx.Bootstrap.setDisplayNames(members, clazz.classname + ".prototype"); + for(var i = 0,a = Object.keys(members),l = a.length;i < l;i++){ + + key = a[i]; + member = members[key]; + { + }; + // Added helper stuff to functions + // Hint: Could not use typeof function because RegExp objects are functions, too + // Protect to apply base property and aspect support on special attributes e.g. + // classes which are function like as well. + if(base !== false && member instanceof Function && member.$$type == null){ + + if(wrap == true){ + + // wrap "patched" mixin member + member = this.__mixinMemberWrapper(member, proto[key]); + } else { + + // Configure extend (named base here) + // Hint: proto[key] is not yet overwritten here + if(proto[key]){ + + member.base = proto[key]; + }; + member.self = clazz; + }; + if(qx.core.Environment.get("qx.aspects")){ + + member = qx.core.Aspect.wrap(clazz.classname + "." + key, member, "member"); + }; + }; + // Attach member + proto[key] = member; + }; + }, + /** + * Wraps a member function of a mixin, which is included using "patch". This + * allows "base" calls in the mixin member function. + * + * @param member {Function} The mixin method to wrap + * @param base {Function} The overwritten method + * @return {Function} the wrapped mixin member + */ + __mixinMemberWrapper : function(member, base){ + + if(base){ + + return function(){ + + var oldBase = member.base; + member.base = base; + var retval = member.apply(this, arguments); + member.base = oldBase; + return retval; + }; + } else { + + return member; + }; + }, + /** + * Add a single interface to a class + * + * @param clazz {Class} class to add interface to + * @param iface {Interface} the Interface to add + */ + __addInterface : function(clazz, iface){ + + { + }; + // Store interface reference + var list = qx.Interface.flatten([iface]); + if(clazz.$$implements){ + + clazz.$$implements.push(iface); + clazz.$$flatImplements.push.apply(clazz.$$flatImplements, list); + } else { + + clazz.$$implements = [iface]; + clazz.$$flatImplements = list; + }; + }, + /** + * Wrap the constructor of an already existing clazz. This function will + * replace all references to the existing constructor with the new wrapped + * constructor. + * + * @param clazz {Class} The class to wrap + * @return {Class} The wrapped class + */ + __retrospectWrapConstruct : function(clazz){ + + var name = clazz.classname; + var wrapper = this.__wrapConstructor(clazz, name, clazz.$$classtype); + // copy all keys from the wrapped constructor to the wrapper + for(var i = 0,a = Object.keys(clazz),l = a.length;i < l;i++){ + + key = a[i]; + wrapper[key] = clazz[key]; + }; + // fix prototype + wrapper.prototype = clazz.prototype; + // fix self references in members + var members = clazz.prototype; + for(var i = 0,a = Object.keys(members),l = a.length;i < l;i++){ + + key = a[i]; + var method = members[key]; + // check if method is available because null values can be stored as + // init values on classes e.g. [BUG #3709] + if(method && method.self == clazz){ + + method.self = wrapper; + }; + }; + // fix base and superclass references in all defined classes + for(var key in this.$$registry){ + + var construct = this.$$registry[key]; + if(!construct){ + + continue; + }; + if(construct.base == clazz){ + + construct.base = wrapper; + }; + if(construct.superclass == clazz){ + + construct.superclass = wrapper; + }; + if(construct.$$original){ + + if(construct.$$original.base == clazz){ + + construct.$$original.base = wrapper; + }; + if(construct.$$original.superclass == clazz){ + + construct.$$original.superclass = wrapper; + }; + }; + }; + qx.Bootstrap.createNamespace(name, wrapper); + this.$$registry[name] = wrapper; + return wrapper; + }, + /** + * Include all features of the mixin into the given class, recursively. + * + * @param clazz {Class} The class onto which the mixin should be attached. + * @param mixin {Mixin} Include all features of this mixin + * @param patch {Boolean} Overwrite existing fields, functions and properties + */ + __addMixin : function(clazz, mixin, patch){ + + { + }; + if(this.hasMixin(clazz, mixin)){ + + return; + }; + var isConstructorWrapped = clazz.$$original; + if(mixin.$$constructor && !isConstructorWrapped){ + + clazz = this.__retrospectWrapConstruct(clazz); + }; + // Attach content + var list = qx.Mixin.flatten([mixin]); + var entry; + for(var i = 0,l = list.length;i < l;i++){ + + entry = list[i]; + // Attach events + if(entry.$$events){ + + this.__addEvents(clazz, entry.$$events, patch); + }; + // Attach properties (Properties are already readonly themselves, no patch handling needed) + if(entry.$$properties){ + + this.__addProperties(clazz, entry.$$properties, patch); + }; + // Attach members (Respect patch setting, but dont apply base variables) + if(entry.$$members){ + + this.__addMembers(clazz, entry.$$members, patch, patch, patch); + }; + }; + // Store mixin reference + if(clazz.$$includes){ + + clazz.$$includes.push(mixin); + clazz.$$flatIncludes.push.apply(clazz.$$flatIncludes, list); + } else { + + clazz.$$includes = [mixin]; + clazz.$$flatIncludes = list; + }; + }, + /* + --------------------------------------------------------------------------- + PRIVATE FUNCTION HELPERS + --------------------------------------------------------------------------- + */ + /** + * Returns the default constructor. + * This constructor just calls the constructor of the base class. + * + * @return {Function} The default constructor. + */ + __createDefaultConstructor : function(){ + + function defaultConstructor(){ + + defaultConstructor.base.apply(this, arguments); + }; + return defaultConstructor; + }, + /** + * Returns an empty function. This is needed to get an empty function with an empty closure. + * + * @return {Function} empty function + */ + __createEmptyFunction : function(){ + + return function(){ + }; + }, + /** + * Checks if the constructor needs to be wrapped. + * + * @param base {Class} The base class. + * @param mixins {Mixin[]} All mixins which should be included. + * @return {Boolean} true, if the constructor needs to be wrapped. + */ + __needsConstructorWrapper : function(base, mixins){ + + { + }; + // Check for base class mixin constructors + if(base && base.$$includes){ + + var baseMixins = base.$$flatIncludes; + for(var i = 0,l = baseMixins.length;i < l;i++){ + + if(baseMixins[i].$$constructor){ + + return true; + }; + }; + }; + // check for direct mixin constructors + if(mixins){ + + var flatMixins = qx.Mixin.flatten(mixins); + for(var i = 0,l = flatMixins.length;i < l;i++){ + + if(flatMixins[i].$$constructor){ + + return true; + }; + }; + }; + return false; + }, + /** + * Generate a wrapper of the original class constructor in order to enable + * some of the advanced OO features (e.g. abstract class, singleton, mixins) + * + * @param construct {Function} the original constructor + * @param name {String} name of the class + * @param type {String} the user specified class type + * @return {Function} The wrapped constructor + */ + __wrapConstructor : function(construct, name, type){ + + var wrapper = function(){ + + var clazz = wrapper; + { + }; + // Execute default constructor + var retval = clazz.$$original.apply(this, arguments); + // Initialize local mixins + if(clazz.$$includes){ + + var mixins = clazz.$$flatIncludes; + for(var i = 0,l = mixins.length;i < l;i++){ + + if(mixins[i].$$constructor){ + + mixins[i].$$constructor.apply(this, arguments); + }; + }; + }; + { + }; + // Return optional return value + return retval; + }; + if(qx.core.Environment.get("qx.aspects")){ + + var aspectWrapper = qx.core.Aspect.wrap(name, wrapper, "constructor"); + wrapper.$$original = construct; + wrapper.constructor = aspectWrapper; + wrapper = aspectWrapper; + }; + // Store original constructor + wrapper.$$original = construct; + // Store wrapper into constructor (needed for base calls etc.) + construct.wrapper = wrapper; + // Return generated wrapper + return wrapper; + } + }, + defer : function(){ + + // Binding of already loaded bootstrap classes + if(qx.core.Environment.get("qx.aspects")){ + + for(var classname in qx.Bootstrap.$$registry){ + + var statics = qx.Bootstrap.$$registry[classname]; + for(var key in statics){ + + // only functions, no regexps + if(statics[key] instanceof Function){ + + statics[key] = qx.core.Aspect.wrap(classname + "." + key, statics[key], "static"); + }; + }; + }; + }; + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Martin Wittemann (martinwittemann) + +************************************************************************ */ +/** + * This mixin is forwarding the static methods of + * {@link qx.data.SingleValueBinding} to the instance including the mixin. + * The source object will be this. + */ +qx.Mixin.define("qx.data.MBinding", { + members : { + /** + * The bind method delegates the call to the + * {@link qx.data.SingleValueBinding#bind} function. As source, the current + * object (this) will be used. + * + * @param sourcePropertyChain {String} The property chain which represents + * the source property. + * @param targetObject {qx.core.Object} The object which the source should + * be bind to. + * @param targetProperty {String} The property name of the target object. + * @param options {Map} A map containing the options. See + * {@link qx.data.SingleValueBinding#bind} for more + * information. + * + * @return {var} Returns the internal id for that binding. This can be used + * for referencing the binding e.g. for removing. This is not an atomic + * id so you can't you use it as a hash-map index. + * + * @throws {qx.core.AssertionError} If the event is no data event or + * there is no property definition for object and property (source and + * target). + */ + bind : function(sourcePropertyChain, targetObject, targetProperty, options){ + + return qx.data.SingleValueBinding.bind(this, sourcePropertyChain, targetObject, targetProperty, options); + }, + /** + * Removes the binding with the given id from the current object. The + * id hast to be the id returned by any of the bind functions. + * + * @param id {var} The id of the binding. + * @throws {Error} If the binding could not be found. + */ + removeBinding : function(id){ + + qx.data.SingleValueBinding.removeBindingFromObject(this, id); + }, + /** + * Removes all bindings from the object. + * + * @throws {qx.core.AssertionError} If the object is not in the internal + * registry of the bindings. + * @throws {Error} If one of the bindings listed internally can not be + * removed. + */ + removeAllBindings : function(){ + + qx.data.SingleValueBinding.removeAllBindingsForObject(this); + }, + /** + * Returns an array which lists all bindings for the object. + * + * @return {Array} An array of binding informations. Every binding + * information is an array itself containing id, sourceObject, sourceEvent, + * targetObject and targetProperty in that order. + */ + getBindings : function(){ + + return qx.data.SingleValueBinding.getAllBindingsForObject(this); + } + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Martin Wittemann (martinwittemann) + +************************************************************************ */ +/** + * The data binding package is still under development so there will be changes + * to the API. This Features is for testing purpose only. + */ +qx.Class.define("qx.data.SingleValueBinding", { + statics : { + /** internal reference for all bindings */ + __bindings : { + }, + /** + * The function is responsible for binding a source objects property to + * a target objects property. Both properties have to have the usual qooxdoo + * getter and setter. The source property also needs to fire change-events + * on every change of its value. + * Please keep in mind, that this binding is unidirectional. If you need + * a binding in both directions, you have to use two of this bindings. + * + * It's also possible to bind some kind of a hierarchy as a source. This + * means that you can separate the source properties with a dot and bind + * by that the object referenced to this property chain. + * Example with an object 'a' which has object 'b' stored in its 'child' + * property. Object b has a string property named abc: + *

+     * qx.data.SingleValueBinding.bind(a, "child.abc", textfield, "value");
+     * 
+ * In that case, if the property abc of b changes, the textfield will + * automatically contain the new value. Also if the child of a changes, the + * new value (abc of the new child) will be in the textfield. + * + * There is also a possibility of binding an array. Therefor the array + * {@link qx.data.IListData} is needed because this array has change events + * which the native does not. Imagine a qooxdoo object a which has a + * children property containing an array holding more of its own kind. + * Every object has a name property as a string. + *
+     * var svb = qx.data.SingleValueBinding;
+     * // bind the first childs name of 'a' to a textfield
+     * svb.bind(a, "children[0].name", textfield, "value");
+     * // bind the last childs name of 'a' to a textfield
+     * svb.bind(a, "children[last].name", textfield2, "value");
+     * // also deeper bindinds are possible
+     * svb.bind(a, "children[0].children[0].name", textfield3, "value");
+     * 
+ * + * As you can see in this example, the abc property of a's b will be bound + * to the textfield. If now the value of b changed or even the a will get a + * new b, the binding still shows the right value. + * + * @param sourceObject {qx.core.Object} The source of the binding. + * @param sourcePropertyChain {String} The property chain which represents + * the source property. + * @param targetObject {qx.core.Object} The object which the source should + * be bind to. + * @param targetPropertyChain {String} The property chain to the target + * object. + * @param options {Map?null} A map containing the options. + *
  • converter: A converter function which takes four parameters + * and should return the converted value. + *
      + *
    1. The data to convert
    2. + *
    3. The corresponding model object, which is only set in case of the use of an controller.
    4. + *
    5. The source object for the binding
    6. + *
    7. The target object.
    8. + *
    + * If no conversion has been done, the given value should be returned. + * e.g. a number to boolean converter + * function(data, model, source, target) {return data > 100;} + *
  • + *
  • onUpdate: A callback function can be given here. This method will be + * called if the binding was updated successful. There will be + * three parameter you do get in that method call. + *
      + *
    1. The source object
    2. + *
    3. The target object
    4. + *
    5. The data
    6. + *
    + * Here is a sample: onUpdate : function(source, target, data) {...} + *
  • + *
  • onSetFail: A callback function can be given here. This method will + * be called if the set of the value fails. + *
  • + *
  • ignoreConverter: A string which will be matched using the current + * property chain. If it matches, the converter will not be called. + *
  • + * + * @return {var} Returns the internal id for that binding. This can be used + * for referencing the binding or e.g. for removing. This is not an atomic + * id so you can't you use it as a hash-map index. + * + * @throws {qx.core.AssertionError} If the event is no data event or + * there is no property definition for object and property (source and + * target). + */ + bind : function(sourceObject, sourcePropertyChain, targetObject, targetPropertyChain, options){ + + { + }; + // set up the target binding + var targetListenerMap = this.__setUpTargetBinding(sourceObject, sourcePropertyChain, targetObject, targetPropertyChain, options); + // get the property names + var propertyNames = sourcePropertyChain.split("."); + // stuff that's needed to store for the listener function + var arrayIndexValues = this.__checkForArrayInPropertyChain(propertyNames); + var sources = []; + var listeners = []; + var listenerIds = []; + var eventNames = []; + var source = sourceObject; + // add a try catch to make it possible to remove the listeners of the + // chain in case the loop breaks after some listeners already added. + try{ + + // go through all property names + for(var i = 0;i < propertyNames.length;i++){ + + // check for the array + if(arrayIndexValues[i] !== ""){ + + // push the array change event + eventNames.push("change"); + } else { + + eventNames.push(this.__getEventNameForProperty(source, propertyNames[i])); + }; + // save the current source + sources[i] = source; + // check for the last property + if(i == propertyNames.length - 1){ + + // if it is an array, set the initial value and bind the event + if(arrayIndexValues[i] !== ""){ + + // getthe current value + var itemIndex = arrayIndexValues[i] === "last" ? source.length - 1 : arrayIndexValues[i]; + var currentValue = source.getItem(itemIndex); + // set the initial value + this.__setInitialValue(currentValue, targetObject, targetPropertyChain, options, sourceObject); + // bind the event + listenerIds[i] = this.__bindEventToProperty(source, eventNames[i], targetObject, targetPropertyChain, options, arrayIndexValues[i]); + } else { + + // try to set the initial value + if(propertyNames[i] != null && source["get" + qx.lang.String.firstUp(propertyNames[i])] != null){ + + var currentValue = source["get" + qx.lang.String.firstUp(propertyNames[i])](); + this.__setInitialValue(currentValue, targetObject, targetPropertyChain, options, sourceObject); + }; + // bind the property + listenerIds[i] = this.__bindEventToProperty(source, eventNames[i], targetObject, targetPropertyChain, options); + }; + } else { + + // create the context for the listener + var context = { + index : i, + propertyNames : propertyNames, + sources : sources, + listenerIds : listenerIds, + arrayIndexValues : arrayIndexValues, + targetObject : targetObject, + targetPropertyChain : targetPropertyChain, + options : options, + listeners : listeners + }; + // create a listener + var listener = qx.lang.Function.bind(this.__chainListener, this, context); + // store the listener for further processing + listeners.push(listener); + // add the chaining listener + listenerIds[i] = source.addListener(eventNames[i], listener); + }; + // get and store the next source + if(source["get" + qx.lang.String.firstUp(propertyNames[i])] == null){ + + source = undefined; + } else if(arrayIndexValues[i] !== ""){ + + var itemIndex = arrayIndexValues[i] === "last" ? source.length - 1 : arrayIndexValues[i]; + source = source["get" + qx.lang.String.firstUp(propertyNames[i])](itemIndex); + } else { + + source = source["get" + qx.lang.String.firstUp(propertyNames[i])](); + // the value should be undefined if we can not find the last part of the property chain + if(source === null && (propertyNames.length - 1) != i){ + + source = undefined; + }; + }; + if(!source){ + + // call the converter if no source could be found on binding creation + this.__setInitialValue(source, targetObject, targetPropertyChain, options, sourceObject); + break; + }; + }; + } catch(ex) { + + // remove the already added listener + // go threw all added listeners (source) + for(var i = 0;i < sources.length;i++){ + + // check if a source is available + if(sources[i] && listenerIds[i]){ + + sources[i].removeListenerById(listenerIds[i]); + }; + }; + var targets = targetListenerMap.targets; + var targetIds = targetListenerMap.listenerIds; + // go threw all added listeners (target) + for(var i = 0;i < targets.length;i++){ + + // check if a target is available + if(targets[i] && targetIds[i]){ + + targets[i].removeListenerById(targetIds[i]); + }; + }; + throw ex; + }; + // create the id map + var id = { + type : "deepBinding", + listenerIds : listenerIds, + sources : sources, + targetListenerIds : targetListenerMap.listenerIds, + targets : targetListenerMap.targets + }; + // store the bindings + this.__storeBinding(id, sourceObject, sourcePropertyChain, targetObject, targetPropertyChain); + return id; + }, + /** + * Event listener for the chaining of the properties. + * + * @param context {Map} The current context for the listener. + */ + __chainListener : function(context){ + + // invoke the onUpdate method + if(context.options && context.options.onUpdate){ + + context.options.onUpdate(context.sources[context.index], context.targetObject); + }; + // delete all listener after the current one + for(var j = context.index + 1;j < context.propertyNames.length;j++){ + + // remove the old sources + var source = context.sources[j]; + context.sources[j] = null; + if(!source){ + + continue; + }; + // remove the listeners + source.removeListenerById(context.listenerIds[j]); + }; + // get the current source + var source = context.sources[context.index]; + // add new once after the current one + for(var j = context.index + 1;j < context.propertyNames.length;j++){ + + // get and store the new source + if(context.arrayIndexValues[j - 1] !== ""){ + + source = source["get" + qx.lang.String.firstUp(context.propertyNames[j - 1])](context.arrayIndexValues[j - 1]); + } else { + + source = source["get" + qx.lang.String.firstUp(context.propertyNames[j - 1])](); + }; + context.sources[j] = source; + // reset the target object if no new source could be found + if(!source){ + + // use the converter if the property chain breaks [BUG# 6880] + if(context.options && context.options.converter){ + + var ignoreConverter = false; + // take care of the ignore pattern used for the controller + if(context.options.ignoreConverter){ + + // the current property chain as string + var currentSourceChain = context.propertyNames.slice(0, j).join("."); + // match for the current patten given in the options + var match = currentSourceChain.match(new RegExp("^" + context.options.ignoreConverter)); + ignoreConverter = match ? match.length > 0 : false; + }; + if(!ignoreConverter){ + + this.__setTargetValue(context.targetObject, context.targetPropertyChain, context.options.converter()); + } else { + + this.__resetTargetValue(context.targetObject, context.targetPropertyChain); + }; + } else { + + this.__resetTargetValue(context.targetObject, context.targetPropertyChain); + }; + break; + }; + // if its the last property + if(j == context.propertyNames.length - 1){ + + // if its an array + if(qx.Class.implementsInterface(source, qx.data.IListData)){ + + // set the inital value + var itemIndex = context.arrayIndexValues[j] === "last" ? source.length - 1 : context.arrayIndexValues[j]; + var currentValue = source.getItem(itemIndex); + this.__setInitialValue(currentValue, context.targetObject, context.targetPropertyChain, context.options, context.sources[context.index]); + // bind the item event to the new target + context.listenerIds[j] = this.__bindEventToProperty(source, "change", context.targetObject, context.targetPropertyChain, context.options, context.arrayIndexValues[j]); + } else { + + if(context.propertyNames[j] != null && source["get" + qx.lang.String.firstUp(context.propertyNames[j])] != null){ + + var currentValue = source["get" + qx.lang.String.firstUp(context.propertyNames[j])](); + this.__setInitialValue(currentValue, context.targetObject, context.targetPropertyChain, context.options, context.sources[context.index]); + }; + var eventName = this.__getEventNameForProperty(source, context.propertyNames[j]); + // bind the last property to the new target + context.listenerIds[j] = this.__bindEventToProperty(source, eventName, context.targetObject, context.targetPropertyChain, context.options); + }; + } else { + + // check if a listener already created + if(context.listeners[j] == null){ + + var listener = qx.lang.Function.bind(this.__chainListener, this, context); + // store the listener for further processing + context.listeners.push(listener); + }; + // add a new listener + if(qx.Class.implementsInterface(source, qx.data.IListData)){ + + var eventName = "change"; + } else { + + var eventName = this.__getEventNameForProperty(source, context.propertyNames[j]); + }; + context.listenerIds[j] = source.addListener(eventName, context.listeners[j]); + }; + }; + }, + /** + * Internal helper for setting up the listening to the changes on the + * target side of the binding. Only works if the target property is a + * property chain + * + * @param sourceObject {qx.core.Object} The source of the binding. + * @param sourcePropertyChain {String} The property chain which represents + * the source property. + * @param targetObject {qx.core.Object} The object which the source should + * be bind to. + * @param targetPropertyChain {String} The property name of the target + * object. + * @param options {Map} The options map perhaps containing the user defined + * converter. + * @return {var} A map containing the listener ids and the targets. + */ + __setUpTargetBinding : function(sourceObject, sourcePropertyChain, targetObject, targetPropertyChain, options){ + + // get the property names + var propertyNames = targetPropertyChain.split("."); + var arrayIndexValues = this.__checkForArrayInPropertyChain(propertyNames); + var targets = []; + var listeners = []; + var listenerIds = []; + var eventNames = []; + var target = targetObject; + // go through all property names + for(var i = 0;i < propertyNames.length - 1;i++){ + + // check for the array + if(arrayIndexValues[i] !== ""){ + + // push the array change event + eventNames.push("change"); + } else { + + try{ + + eventNames.push(this.__getEventNameForProperty(target, propertyNames[i])); + } catch(e) { + + // if the event names could not be terminated, + // just ignore the target chain listening + break; + }; + }; + // save the current source + targets[i] = target; + // create a listener + var listener = function(){ + + // delete all listener after the current one + for(var j = i + 1;j < propertyNames.length - 1;j++){ + + // remove the old sources + var target = targets[j]; + targets[j] = null; + if(!target){ + + continue; + }; + // remove the listeners + target.removeListenerById(listenerIds[j]); + }; + // get the current target + var target = targets[i]; + // add new once after the current one + for(var j = i + 1;j < propertyNames.length - 1;j++){ + + var firstUpPropName = qx.lang.String.firstUp(propertyNames[j - 1]); + // get and store the new target + if(arrayIndexValues[j - 1] !== ""){ + + var currentIndex = arrayIndexValues[j - 1] === "last" ? target.getLength() - 1 : arrayIndexValues[j - 1]; + target = target["get" + firstUpPropName](currentIndex); + } else { + + target = target["get" + firstUpPropName](); + }; + targets[j] = target; + // check if a listener already created + if(listeners[j] == null){ + + // store the listener for further processing + listeners.push(listener); + }; + // add a new listener + if(qx.Class.implementsInterface(target, qx.data.IListData)){ + + var eventName = "change"; + } else { + + try{ + + var eventName = qx.data.SingleValueBinding.__getEventNameForProperty(target, propertyNames[j]); + } catch(e) { + + // if the event name could not be terminated, + // ignore the rest + break; + }; + }; + listenerIds[j] = target.addListener(eventName, listeners[j]); + }; + qx.data.SingleValueBinding.updateTarget(sourceObject, sourcePropertyChain, targetObject, targetPropertyChain, options); + }; + // store the listener for further processing + listeners.push(listener); + // add the chaining listener + listenerIds[i] = target.addListener(eventNames[i], listener); + var firstUpPropName = qx.lang.String.firstUp(propertyNames[i]); + // get and store the next target + if(target["get" + firstUpPropName] == null){ + + target = null; + } else if(arrayIndexValues[i] !== ""){ + + target = target["get" + firstUpPropName](arrayIndexValues[i]); + } else { + + target = target["get" + firstUpPropName](); + }; + if(!target){ + + break; + }; + }; + return { + listenerIds : listenerIds, + targets : targets + }; + }, + /** + * Helper for updating the target. Gets the current set data from the source + * and set that on the target. + * + * @param sourceObject {qx.core.Object} The source of the binding. + * @param sourcePropertyChain {String} The property chain which represents + * the source property. + * @param targetObject {qx.core.Object} The object which the source should + * be bind to. + * @param targetPropertyChain {String} The property name of the target + * object. + * @param options {Map} The options map perhaps containing the user defined + * converter. + * + * @internal + */ + updateTarget : function(sourceObject, sourcePropertyChain, targetObject, targetPropertyChain, options){ + + var value = this.resolvePropertyChain(sourceObject, sourcePropertyChain); + // convert the data before setting + value = qx.data.SingleValueBinding.__convertValue(value, targetObject, targetPropertyChain, options, sourceObject); + this.__setTargetValue(targetObject, targetPropertyChain, value); + }, + /** + * Internal helper for getting the current set value at the property chain. + * + * @param o {qx.core.Object} The source of the binding. + * @param propertyChain {String} The property chain which represents + * the source property. + * @return {var?undefined} Returns the set value if defined. + */ + resolvePropertyChain : function(o, propertyChain){ + + var source = this.__getTargetFromChain(o, propertyChain); + var value; + if(source != null){ + + // geht the name of the last property + var lastProperty = propertyChain.substring(propertyChain.lastIndexOf(".") + 1, propertyChain.length); + // check for arrays + if(lastProperty.charAt(lastProperty.length - 1) == "]"){ + + // split up the chain into property and index + var index = lastProperty.substring(lastProperty.lastIndexOf("[") + 1, lastProperty.length - 1); + var prop = lastProperty.substring(0, lastProperty.lastIndexOf("[")); + // get the array + var sourceArray = source["get" + qx.lang.String.firstUp(prop)](); + if(index == "last"){ + + index = sourceArray.length - 1; + }; + if(sourceArray != null){ + + value = sourceArray.getItem(index); + }; + } else { + + // set the given value + value = source["get" + qx.lang.String.firstUp(lastProperty)](); + }; + }; + return value; + }, + /** + * Tries to return a fitting event name to the given source object and + * property name. First, it assumes that the propertyname is a real property + * and therefore it checks the property definition for the event. The second + * possibility is to check if there is an event with the given name. The + * third and last possibility checked is if there is an event which is named + * change + propertyname. If this three possibilities fail, an error will be + * thrown. + * + * @param source {qx.core.Object} The source where the property is stored. + * @param propertyname {String} The name of the property. + * @return {String} The name of the corresponding property. + */ + __getEventNameForProperty : function(source, propertyname){ + + // get the current event Name from the property definition + var eventName = this.__getEventForProperty(source, propertyname); + // if no event name could be found + if(eventName == null){ + + // check if the propertyname is the event name + if(qx.Class.supportsEvent(source.constructor, propertyname)){ + + eventName = propertyname; + } else if(qx.Class.supportsEvent(source.constructor, "change" + qx.lang.String.firstUp(propertyname))){ + + eventName = "change" + qx.lang.String.firstUp(propertyname); + } else { + + throw new qx.core.AssertionError("Binding property " + propertyname + " of object " + source + " not possible: No event available. "); + }; + }; + return eventName; + }, + /** + * Resets the value of the given target after resolving the target property + * chain. + * + * @param targetObject {qx.core.Object} The object where the property chain + * starts. + * @param targetPropertyChain {String} The names of the properties, + * separated with a dot. + */ + __resetTargetValue : function(targetObject, targetPropertyChain){ + + // get the last target object of the chain + var target = this.__getTargetFromChain(targetObject, targetPropertyChain); + if(target != null){ + + // get the name of the last property + var lastProperty = targetPropertyChain.substring(targetPropertyChain.lastIndexOf(".") + 1, targetPropertyChain.length); + // check for an array and set the value to null + if(lastProperty.charAt(lastProperty.length - 1) == "]"){ + + this.__setTargetValue(targetObject, targetPropertyChain, null); + return; + }; + // try to reset the property + if(target["reset" + qx.lang.String.firstUp(lastProperty)] != undefined){ + + target["reset" + qx.lang.String.firstUp(lastProperty)](); + } else { + + // fallback if no resetter is given (see bug #2456) + target["set" + qx.lang.String.firstUp(lastProperty)](null); + }; + }; + }, + /** + * Sets the given value to the given target after resolving the + * target property chain. + * + * @param targetObject {qx.core.Object} The object where the property chain + * starts. + * @param targetPropertyChain {String} The names of the properties, + * separated with a dot. + * @param value {var} The value to set. + */ + __setTargetValue : function(targetObject, targetPropertyChain, value){ + + // get the last target object of the chain + var target = this.__getTargetFromChain(targetObject, targetPropertyChain); + if(target != null){ + + // geht the name of the last property + var lastProperty = targetPropertyChain.substring(targetPropertyChain.lastIndexOf(".") + 1, targetPropertyChain.length); + // check for arrays + if(lastProperty.charAt(lastProperty.length - 1) == "]"){ + + // split up the chain into property and index + var index = lastProperty.substring(lastProperty.lastIndexOf("[") + 1, lastProperty.length - 1); + var prop = lastProperty.substring(0, lastProperty.lastIndexOf("[")); + // get the array + var targetArray = targetObject; + if(!qx.Class.implementsInterface(targetArray, qx.data.IListData)){ + + targetArray = target["get" + qx.lang.String.firstUp(prop)](); + }; + if(index == "last"){ + + index = targetArray.length - 1; + }; + if(targetArray != null){ + + targetArray.setItem(index, value); + }; + } else { + + // set the given value + target["set" + qx.lang.String.firstUp(lastProperty)](value); + }; + }; + }, + /** + * Helper-Function resolving the object on which the last property of the + * chain should be set. + * + * @param targetObject {qx.core.Object} The object where the property chain + * starts. + * @param targetPropertyChain {String} The names of the properties, + * separated with a dot. + * @return {qx.core.Object | null} The object on which the last property + * should be set. + */ + __getTargetFromChain : function(targetObject, targetPropertyChain){ + + var properties = targetPropertyChain.split("."); + var target = targetObject; + // ignore the last property + for(var i = 0;i < properties.length - 1;i++){ + + try{ + + var property = properties[i]; + // if there is an array notation + if(property.indexOf("]") == property.length - 1){ + + var index = property.substring(property.indexOf("[") + 1, property.length - 1); + property = property.substring(0, property.indexOf("[")); + }; + // in case there is a property infront of the brackets + if(property != ""){ + + target = target["get" + qx.lang.String.firstUp(property)](); + }; + // if there is an index, we can be sure its an array + if(index != null){ + + // check for the 'last' notation + if(index == "last"){ + + index = target.length - 1; + }; + // get the array item + target = target.getItem(index); + index = null; + }; + } catch(ex) { + + return null; + }; + }; + return target; + }, + /** + * Set the given value to the target property. This method is used for + * initially set the value. + * + * @param value {var} The value to set. + * @param targetObject {qx.core.Object} The object which contains the target + * property. + * @param targetPropertyChain {String} The name of the target property in the + * target object. + * @param options {Map} The options map perhaps containing the user defined + * converter. + * @param sourceObject {qx.core.Object} The source object of the binding ( + * used for the onUpdate callback). + */ + __setInitialValue : function(value, targetObject, targetPropertyChain, options, sourceObject){ + + // first convert the initial value + value = this.__convertValue(value, targetObject, targetPropertyChain, options, sourceObject); + // check if the converted value is undefined + if(value === undefined){ + + this.__resetTargetValue(targetObject, targetPropertyChain); + }; + // only set the initial value if one is given (may be null) + if(value !== undefined){ + + try{ + + this.__setTargetValue(targetObject, targetPropertyChain, value); + // tell the user that the setter was invoked probably + if(options && options.onUpdate){ + + options.onUpdate(sourceObject, targetObject, value); + }; + } catch(e) { + + if(!(e instanceof qx.core.ValidationError)){ + + throw e; + }; + if(options && options.onSetFail){ + + options.onSetFail(e); + } else { + + qx.log.Logger.warn("Failed so set value " + value + " on " + targetObject + ". Error message: " + e); + }; + }; + }; + }, + /** + * Checks for an array element in the given property names and adapts the + * arrays to fit the algorithm. + * + * @param propertyNames {Array} The array containing the property names. + * Attention, this method can chang this parameter!!! + * @return {Array} An array containing the values of the array properties + * corresponding to the property names. + */ + __checkForArrayInPropertyChain : function(propertyNames){ + + // array for the values of the array properties + var arrayIndexValues = []; + // go through all properties and check for array notations + for(var i = 0;i < propertyNames.length;i++){ + + var name = propertyNames[i]; + // if its an array property in the chain + if(qx.lang.String.endsWith(name, "]")){ + + // get the inner value of the array notation + var arrayIndex = name.substring(name.indexOf("[") + 1, name.indexOf("]")); + // check the arrayIndex + if(name.indexOf("]") != name.length - 1){ + + throw new Error("Please use only one array at a time: " + name + " does not work."); + }; + if(arrayIndex !== "last"){ + + if(arrayIndex == "" || isNaN(parseInt(arrayIndex, 10))){ + + throw new Error('No number or \'last\' value hast been given in an array binding: ' + name + " does not work."); + }; + }; + // if a property is infront of the array notation + if(name.indexOf("[") != 0){ + + // store the property name without the array notation + propertyNames[i] = name.substring(0, name.indexOf("[")); + // store the values in the array for the current iteration + arrayIndexValues[i] = ""; + // store the properties for the next iteration (the item of the array) + arrayIndexValues[i + 1] = arrayIndex; + propertyNames.splice(i + 1, 0, "item"); + // skip the next iteration. its the array item and its already set + i++; + } else { + + // store the array index and override the entry in the property names + arrayIndexValues[i] = arrayIndex; + propertyNames.splice(i, 1, "item"); + }; + } else { + + arrayIndexValues[i] = ""; + }; + }; + return arrayIndexValues; + }, + /** + * Internal helper method which is actually doing all bindings. That means + * that an event listener will be added to the source object which listens + * to the given event and invokes an set on the target property on the + * targetObject. + * This method does not store the binding in the internal reference store + * so it should NOT be used from outside this class. For an outside usage, + * use {@link #bind}. + * + * @param sourceObject {qx.core.Object} The source of the binding. + * @param sourceEvent {String} The event of the source object which could + * be the change event in common but has to be an + * {@link qx.event.type.Data} event. + * @param targetObject {qx.core.Object} The object which the source should + * be bind to. + * @param targetProperty {String} The property name of the target object. + * @param options {Map} A map containing the options. See + * {@link #bind} for more information. + * @param arrayIndex {String} The index of the given array if its an array + * to bind. + * + * @return {var} Returns the internal id for that binding. This can be used + * for referencing the binding or e.g. for removing. This is not an atomic + * id so you can't you use it as a hash-map index. It's the id which will + * be returned by the {@link qx.core.Object#addListener} method. + * @throws {qx.core.AssertionError} If the event is no data event or + * there is no property definition for the target object and target + * property. + */ + __bindEventToProperty : function(sourceObject, sourceEvent, targetObject, targetProperty, options, arrayIndex){ + + { + + var eventType; + }; + var bindListener = function(arrayIndex, e){ + + // if an array value is given + if(arrayIndex !== ""){ + + //check if its the "last" value + if(arrayIndex === "last"){ + + arrayIndex = sourceObject.length - 1; + }; + // get the data of the array + var data = sourceObject.getItem(arrayIndex); + // reset the target if the data is not set + if(data === undefined){ + + qx.data.SingleValueBinding.__resetTargetValue(targetObject, targetProperty); + }; + // only do something if the curren array has been changed + var start = e.getData().start; + var end = e.getData().end; + if(arrayIndex < start || arrayIndex > end){ + + return; + }; + } else { + + // get the data out of the event + var data = e.getData(); + }; + { + }; + // convert the data + data = qx.data.SingleValueBinding.__convertValue(data, targetObject, targetProperty, options, sourceObject); + { + }; + // try to set the value + try{ + + if(data !== undefined){ + + qx.data.SingleValueBinding.__setTargetValue(targetObject, targetProperty, data); + } else { + + qx.data.SingleValueBinding.__resetTargetValue(targetObject, targetProperty); + }; + // tell the user that the setter was invoked probably + if(options && options.onUpdate){ + + options.onUpdate(sourceObject, targetObject, data); + }; + } catch(ex) { + + if(!(ex instanceof qx.core.ValidationError)){ + + throw ex; + }; + if(options && options.onSetFail){ + + options.onSetFail(ex); + } else { + + qx.log.Logger.warn("Failed so set value " + data + " on " + targetObject + ". Error message: " + ex); + }; + }; + }; + // check if an array index is given + if(!arrayIndex){ + + // if not, signal it a s an empty string + arrayIndex = ""; + }; + // bind the listener function (make the array index in the listener available) + bindListener = qx.lang.Function.bind(bindListener, sourceObject, arrayIndex); + // add the listener + var id = sourceObject.addListener(sourceEvent, bindListener); + return id; + }, + /** + * This method stores the given value as a binding in the internal structure + * of all bindings. + * + * @param id {var} The listener id of the id for a deeper binding. + * @param sourceObject {qx.core.Object} The source Object of the binding. + * @param sourceEvent {String} The name of the source event. + * @param targetObject {qx.core.Object} The target object. + * @param targetProperty {String} The name of the property on the target + * object. + */ + __storeBinding : function(id, sourceObject, sourceEvent, targetObject, targetProperty){ + + // add the listener id to the internal registry + if(this.__bindings[sourceObject.toHashCode()] === undefined){ + + this.__bindings[sourceObject.toHashCode()] = []; + }; + this.__bindings[sourceObject.toHashCode()].push([id, sourceObject, sourceEvent, targetObject, targetProperty]); + }, + /** + * This method takes the given value, checks if the user has given a + * converter and converts the value to its target type. If no converter is + * given by the user, the {@link #__defaultConversion} will try to convert + * the value. + * + * @param value {var} The value which possibly should be converted. + * @param targetObject {qx.core.Object} The target object. + * @param targetPropertyChain {String} The property name of the target object. + * @param options {Map} The options map which can includes the converter. + * For a detailed information on the map, take a look at + * {@link #bind}. + * @param sourceObject {qx.core.Object} The source obejct for the binding. + * + * @return {var} The converted value. If no conversion has been done, the + * value property will be returned. + * @throws {qx.core.AssertionError} If there is no property definition + * of the given target object and target property. + */ + __convertValue : function(value, targetObject, targetPropertyChain, options, sourceObject){ + + // do the conversion given by the user + if(options && options.converter){ + + var model; + if(targetObject.getModel){ + + model = targetObject.getModel(); + }; + return options.converter(value, model, sourceObject, targetObject); + } else { + + var target = this.__getTargetFromChain(targetObject, targetPropertyChain); + var lastProperty = targetPropertyChain.substring(targetPropertyChain.lastIndexOf(".") + 1, targetPropertyChain.length); + // if no target is currently available, return the original value + if(target == null){ + + return value; + }; + var propertieDefinition = qx.Class.getPropertyDefinition(target.constructor, lastProperty); + var check = propertieDefinition == null ? "" : propertieDefinition.check; + return this.__defaultConversion(value, check); + }; + }, + /** + * Helper method which tries to figure out if the given property on the + * given object does have a change event and if returns the name of it. + * + * @param sourceObject {qx.core.Object} The object to check. + * @param sourceProperty {String} The name of the property. + * + * @return {String} The name of the change event. + * @throws {qx.core.AssertionError} If there is no property definition of + * the given object property pair. + */ + __getEventForProperty : function(sourceObject, sourceProperty){ + + // get the event name + var propertieDefinition = qx.Class.getPropertyDefinition(sourceObject.constructor, sourceProperty); + if(propertieDefinition == null){ + + return null; + }; + return propertieDefinition.event; + }, + /** + * Tries to convert the data to the type given in the targetCheck argument. + * + * @param data {var} The data to convert. + * @param targetCheck {String} The value of the check property. That usually + * contains the target type. + * @return {Integer|String|Float} The converted data + */ + __defaultConversion : function(data, targetCheck){ + + var dataType = qx.lang.Type.getClass(data); + // to integer + if((dataType == "Number" || dataType == "String") && (targetCheck == "Integer" || targetCheck == "PositiveInteger")){ + + data = parseInt(data, 10); + }; + // to string + if((dataType == "Boolean" || dataType == "Number" || dataType == "Date") && targetCheck == "String"){ + + data = data + ""; + }; + // to float + if((dataType == "Number" || dataType == "String") && (targetCheck == "Number" || targetCheck == "PositiveNumber")){ + + data = parseFloat(data); + }; + return data; + }, + /** + * Removes the binding with the given id from the given sourceObject. The + * id hast to be the id returned by any of the bind functions. + * + * @param sourceObject {qx.core.Object} The source object of the binding. + * @param id {var} The id of the binding. + * @throws {Error} If the binding could not be found. + */ + removeBindingFromObject : function(sourceObject, id){ + + // check for a deep binding + if(id.type == "deepBinding"){ + + // go threw all added listeners (source) + for(var i = 0;i < id.sources.length;i++){ + + // check if a source is available + if(id.sources[i]){ + + id.sources[i].removeListenerById(id.listenerIds[i]); + }; + }; + // go threw all added listeners (target) + for(var i = 0;i < id.targets.length;i++){ + + // check if a target is available + if(id.targets[i]){ + + id.targets[i].removeListenerById(id.targetListenerIds[i]); + }; + }; + } else { + + // remove the listener + sourceObject.removeListenerById(id); + }; + // remove the id from the internal reference system + var bindings = this.__bindings[sourceObject.toHashCode()]; + // check if the binding exists + if(bindings != undefined){ + + for(var i = 0;i < bindings.length;i++){ + + if(bindings[i][0] == id){ + + qx.lang.Array.remove(bindings, bindings[i]); + return; + }; + }; + }; + throw new Error("Binding could not be found!"); + }, + /** + * Removes all bindings for the given object. + * + * @param object {qx.core.Object} The object of which the bindings should be + * removed. + * @throws {qx.core.AssertionError} If the object is not in the internal + * registry of the bindings. + * @throws {Error} If one of the bindings listed internally can not be + * removed. + */ + removeAllBindingsForObject : function(object){ + + { + }; + // get the bindings + var bindings = this.__bindings[object.toHashCode()]; + if(bindings != undefined){ + + // remove every binding with the removeBindingFromObject function + for(var i = bindings.length - 1;i >= 0;i--){ + + this.removeBindingFromObject(object, bindings[i][0]); + }; + }; + }, + /** + * Returns an array which lists all bindings. + * + * @param object {qx.core.Object} The object of which the bindings should + * be returned. + * + * @return {Array} An array of binding informations. Every binding + * information is an array itself containing id, sourceObject, + * sourceEvent, targetObject and targetProperty in that order. + */ + getAllBindingsForObject : function(object){ + + // create an empty array if no binding exists + if(this.__bindings[object.toHashCode()] === undefined){ + + this.__bindings[object.toHashCode()] = []; + }; + return this.__bindings[object.toHashCode()]; + }, + /** + * Removes all binding in the whole application. After that not a single + * binding is left. + */ + removeAllBindings : function(){ + + // go threw all registerd objects + for(var hash in this.__bindings){ + + var object = qx.core.ObjectRegistry.fromHashCode(hash); + // check for the object, perhaps its already deleted + if(object == null){ + + delete this.__bindings[hash]; + continue; + }; + this.removeAllBindingsForObject(object); + }; + // reset the bindings map + this.__bindings = { + }; + }, + /** + * Returns a map containing for every bound object an array of data binding + * information. The key of the map is the hashcode of the bound objects. + * Every binding is represented by an array containing id, sourceObject, + * sourceEvent, targetObject and targetProperty. + * + * @return {Map} Map containing all bindings. + */ + getAllBindings : function(){ + + return this.__bindings; + }, + /** + * Debug function which shows some valuable information about the given + * binding in console. For that it uses {@link qx.log.Logger}. + * + * @param object {qx.core.Object} the source of the binding. + * @param id {var} The id of the binding. + */ + showBindingInLog : function(object, id){ + + var binding; + // go threw all bindings of the given object + for(var i = 0;i < this.__bindings[object.toHashCode()].length;i++){ + + // the first array item is the id + if(this.__bindings[object.toHashCode()][i][0] == id){ + + binding = this.__bindings[object.toHashCode()][i]; + break; + }; + }; + if(binding === undefined){ + + var message = "Binding does not exist!"; + } else { + + var message = "Binding from '" + binding[1] + "' (" + binding[2] + ") to the object '" + binding[3] + "' (" + binding[4] + ")."; + }; + qx.log.Logger.debug(message); + }, + /** + * Debug function which shows all bindings in the log console. To get only + * one binding in the console use {@link #showBindingInLog} + */ + showAllBindingsInLog : function(){ + + // go threw all objects in the registry + for(var hash in this.__bindings){ + + var object = qx.core.ObjectRegistry.fromHashCode(hash); + for(var i = 0;i < this.__bindings[hash].length;i++){ + + this.showBindingInLog(object, this.__bindings[hash][i][0]); + }; + }; + } + } +}); + +/* ************************************************************************ + + qooxdoo - the new era of web development + + http://qooxdoo.org + + Copyright: + 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de + + License: + LGPL: http://www.gnu.org/licenses/lgpl.html + EPL: http://www.eclipse.org/org/documents/epl-v10.php + See the LICENSE file in the project's top-level directory for details. + + Authors: + * Sebastian Werner (wpbasti) + * Andreas Ecker (ecker) + + ====================================================================== + + This class contains code based on the following work: + + * Mootools + http://mootools.net/ + Version 1.1.1 + + Copyright: + (c) 2007 Valerio Proietti + + License: + MIT: http://www.opensource.org/licenses/mit-license.php + + and + + * XRegExp + http://xregexp.com/ + Version 1.5 + + Copyright: + (c) 2006-2007, Steven Levithan + + License: + MIT: http://www.opensource.org/licenses/mit-license.php + + Authors: + * Steven Levithan + +************************************************************************ */ +/** + * String helper functions + * + * The native JavaScript String is not modified by this class. However, + * there are modifications to the native String in {@link qx.lang.normalize.String} for + * browsers that do not support certain features. + * + * @require(qx.lang.normalize.String) + */ +qx.Bootstrap.define("qx.lang.String", { + statics : { + /** + * Unicode letters. they are taken from Steve Levithan's excellent XRegExp library [http://xregexp.com/addons/unicode/unicode-base.js] + */ + __unicodeLetters : "0041-005A0061-007A00AA00B500BA00C0-00D600D8-00F600F8-02C102C6-02D102E0-02E402EC02EE0370-037403760377037A-037D03860388-038A038C038E-03A103A3-03F503F7-0481048A-05250531-055605590561-058705D0-05EA05F0-05F20621-064A066E066F0671-06D306D506E506E606EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA07F407F507FA0800-0815081A082408280904-0939093D09500958-0961097109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E460E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EC60EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10A0-10C510D0-10FA10FC1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317D717DC1820-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541AA71B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C7D1CE9-1CEC1CEE-1CF11D00-1DBF1E00-1F151F18-1F1D1F20-1F451F48-1F4D1F50-1F571F591F5B1F5D1F5F-1F7D1F80-1FB41FB6-1FBC1FBE1FC2-1FC41FC6-1FCC1FD0-1FD31FD6-1FDB1FE0-1FEC1FF2-1FF41FF6-1FFC2071207F2090-209421022107210A-211321152119-211D212421262128212A-212D212F-2139213C-213F2145-2149214E218321842C00-2C2E2C30-2C5E2C60-2CE42CEB-2CEE2D00-2D252D30-2D652D6F2D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE2E2F300530063031-3035303B303C3041-3096309D-309F30A1-30FA30FC-30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A48CA4D0-A4FDA500-A60CA610-A61FA62AA62BA640-A65FA662-A66EA67F-A697A6A0-A6E5A717-A71FA722-A788A78BA78CA7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2A9CFAA00-AA28AA40-AA42AA44-AA4BAA60-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADB-AADDABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB00-FB06FB13-FB17FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF21-FF3AFF41-FF5AFF66-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC", + /** + * A RegExp that matches the first letter in a word - unicode aware + */ + __unicodeFirstLetterInWordRegexp : null, + /** + * @type {Map} Cache for often used string operations [camelCasing and hyphenation] + * e.g. marginTop => margin-top + */ + __stringsMap : { + }, + /** + * Converts a hyphenated string (separated by '-') to camel case. + * + * Example: + *
    qx.lang.String.camelCase("I-like-cookies"); //returns "ILikeCookies"
    + * + * @param str {String} hyphenated string + * @return {String} camelcase string + */ + camelCase : function(str){ + + var result = this.__stringsMap[str]; + if(!result){ + + result = str.replace(/\-([a-z])/g, function(match, chr){ + + return chr.toUpperCase(); + }); + if(str.indexOf("-") >= 0){ + + this.__stringsMap[str] = result; + }; + }; + return result; + }, + /** + * Converts a camelcased string to a hyphenated (separated by '-') string. + * + * Example: + *
    qx.lang.String.hyphenate("weLikeCookies"); //returns "we-like-cookies"
    + * + * @param str {String} camelcased string + * @return {String} hyphenated string + */ + hyphenate : function(str){ + + var result = this.__stringsMap[str]; + if(!result){ + + result = str.replace(/[A-Z]/g, function(match){ + + return ('-' + match.charAt(0).toLowerCase()); + }); + if(str.indexOf("-") == -1){ + + this.__stringsMap[str] = result; + }; + }; + return result; + }, + /** + * Converts a string to camel case. + * + * Example: + *
    qx.lang.String.camelCase("i like cookies"); //returns "I Like Cookies"
    + * + * @param str {String} any string + * @return {String} capitalized string + */ + capitalize : function(str){ + + if(this.__unicodeFirstLetterInWordRegexp === null){ + + var unicodeEscapePrefix = '\\u'; + this.__unicodeFirstLetterInWordRegexp = new RegExp("(^|[^" + this.__unicodeLetters.replace(/[0-9A-F]{4}/g, function(match){ + + return unicodeEscapePrefix + match; + }) + "])[" + this.__unicodeLetters.replace(/[0-9A-F]{4}/g, function(match){ + + return unicodeEscapePrefix + match; + }) + "]", "g"); + }; + return str.replace(this.__unicodeFirstLetterInWordRegexp, function(match){ + + return match.toUpperCase(); + }); + }, + /** + * Removes all extraneous whitespace from a string and trims it + * + * Example: + * + * + * qx.lang.String.clean(" i like cookies \n\n"); + * + * + * Returns "i like cookies" + * + * @param str {String} the string to clean up + * @return {String} Cleaned up string + */ + clean : function(str){ + + return str.replace(/\s+/g, ' ').trim(); + }, + /** + * removes white space from the left side of a string + * + * @param str {String} the string to trim + * @return {String} the trimmed string + */ + trimLeft : function(str){ + + return str.replace(/^\s+/, ""); + }, + /** + * removes white space from the right side of a string + * + * @param str {String} the string to trim + * @return {String} the trimmed string + */ + trimRight : function(str){ + + return str.replace(/\s+$/, ""); + }, + /** + * Check whether the string starts with the given substring + * + * @param fullstr {String} the string to search in + * @param substr {String} the substring to look for + * @return {Boolean} whether the string starts with the given substring + */ + startsWith : function(fullstr, substr){ + + return fullstr.indexOf(substr) === 0; + }, + /** + * Check whether the string ends with the given substring + * + * @param fullstr {String} the string to search in + * @param substr {String} the substring to look for + * @return {Boolean} whether the string ends with the given substring + */ + endsWith : function(fullstr, substr){ + + return fullstr.substring(fullstr.length - substr.length, fullstr.length) === substr; + }, + /** + * Returns a string, which repeats a string 'length' times + * + * @param str {String} string used to repeat + * @param times {Integer} the number of repetitions + * @return {String} repeated string + */ + repeat : function(str, times){ + + return str.length > 0 ? new Array(times + 1).join(str) : ""; + }, + /** + * Pad a string up to a given length. Padding characters are added to the left of the string. + * + * @param str {String} the string to pad + * @param length {Integer} the final length of the string + * @param ch {String} character used to fill up the string + * @return {String} padded string + */ + pad : function(str, length, ch){ + + var padLength = length - str.length; + if(padLength > 0){ + + if(typeof ch === "undefined"){ + + ch = "0"; + }; + return this.repeat(ch, padLength) + str; + } else { + + return str; + }; + }, + /** + * Convert the first character of the string to upper case. + * + * @signature function(str) + * @param str {String} the string + * @return {String} the string with an upper case first character + */ + firstUp : qx.Bootstrap.firstUp, + /** + * Convert the first character of the string to lower case. + * + * @signature function(str) + * @param str {String} the string + * @return {String} the string with a lower case first character + */ + firstLow : qx.Bootstrap.firstLow, + /** + * Check whether the string contains a given substring + * + * @param str {String} the string + * @param substring {String} substring to search for + * @return {Boolean} whether the string contains the substring + */ + contains : function(str, substring){ + + return str.indexOf(substring) != -1; + }, + /** + * Print a list of arguments using a format string + * In the format string occurrences of %n are replaced by the n'th element of the args list. + * Example: + *
    qx.lang.String.format("Hello %1, my name is %2", ["Egon", "Franz"]) == "Hello Egon, my name is Franz"
    + * + * @param pattern {String} format string + * @param args {Array} array of arguments to insert into the format string + * @return {String} the formatted string + */ + format : function(pattern, args){ + + var str = pattern; + var i = args.length; + while(i--){ + + // be sure to always use a string for replacement. + str = str.replace(new RegExp("%" + (i + 1), "g"), args[i] + ""); + }; + return str; + }, + /** + * Escapes all chars that have a special meaning in regular expressions + * + * @param str {String} the string where to escape the chars. + * @return {String} the string with the escaped chars. + */ + escapeRegexpChars : function(str){ + + return str.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1'); + }, + /** + * Converts a string to an array of characters. + *
    "hello" => [ "h", "e", "l", "l", "o" ];
    + * + * @param str {String} the string which should be split + * @return {Array} the result array of characters + */ + toArray : function(str){ + + return str.split(/\B|\b/g); + }, + /** + * Remove HTML/XML tags from a string + * Example: + *
    qx.lang.String.stripTags("<h1>Hello</h1>") == "Hello"
    + * + * @param str {String} string containing tags + * @return {String} the string with stripped tags + */ + stripTags : function(str){ + + return str.replace(/<\/?[^>]+>/gi, ""); + }, + /** + * Strips