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:
+ *
+ *
Name
Type
Description
+ *
extend
Class
The super class the current class inherits from.
+ *
construct
Function
The constructor of the class.
+ *
statics
Map
Map of static values / functions of the class.
+ *
members
Map
Map of instance members of the class.
+ *
defer
Function
Function 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*
+ *
+ *
+ * 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.
+ *
+ *
currentValue The current element being processed in the array.
+ *
index The index of the current element being processed in the array.
+ *
array The array map was called upon.
+ *
+ * @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:
+ *
+ *
previousValue The value previously returned in
+ * the last invocation of the callback, or initialValue, if supplied.
+ * (See below.)
+ *
currentValue The current element being processed in the array.
+ *
index The index of the current element being processed in the array.
+ *
array The array reduce was called upon.
+ *
+ * @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:
+ *
+ *
previousValue The value previously returned in
+ * the last invocation of the callback, or initialValue, if supplied.
+ * (See below.)
+ *
currentValue The current element being processed in the array.
+ *
index The index of the current element being processed in the array.
+ *
array The array reduce was called upon.
+ *
+ * @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:
+ *
+ *
+ *
Name
Type
Description
+ *
+ *
extend
Class
The super class the current class inherits from.
+ *
construct
Function
The constructor of the class.
+ *
statics
Map
Map of static values / functions of the class.
+ *
members
Map
Map of instance members of the class.
+ *
defer
Function
Function 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:
+ *
+ *
+ * @param name {String} name of the mixin
+ * @param config {Map ? null} Mixin definition structure. The configuration map has the following keys:
+ *
+ *
Name
Type
Description
+ *
construct
Function
An optional mixin constructor. It is called on instantiation each
+ * class including this mixin. The constructor takes no parameters.
+ *
destruct
Function
An optional mixin destructor.
+ *
include
Mixin[]
Array of mixins, which will be merged into the mixin.
+ *
statics
Map
+ * 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}).
+ *
+ *
members
Map
Map of members of the mixin.
+ *
properties
Map
Map of property definitions. For a description of the format of a property definition see
+ * {@link qx.core.Property}.
+ *
events
Map
+ * 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:
+ *
+ *
+ *
+ * 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:
+ *
+ *
+ * @param name {String} name of the interface
+ * @param config {Map ? null} Interface definition structure. The configuration map has the following keys:
+ *
+ *
Name
Type
Description
+ *
extend
Interface | Interface[]
Single interface or array of interfaces this interface inherits from.
+ *
members
Map
Map of members of the interface.
+ *
statics
Map
+ * 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}).
+ *
+ *
properties
Map
Map of properties and their definitions.
+ *
events
Map
Map 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:
+ *
+ *
+ *
Name
Type
Description
+ *
check
Array, 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"
+ * 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.
+ *
+ *
apply
String
+ * 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).
+ *
+ *
event
String
+ * On change of the property value an event with the given name will be dispatched. The event type is
+ * {@link qx.event.type.Data}.
+ *
+ *
themeable
Boolean
+ * Whether this property can be set using themes.
+ *
+ *
inheritable
Boolean
+ * 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.
+ *
+ *
nullable
Boolean
+ * Whether null is an allowed value of the property. This is complementary to the check
+ * defined using the check key.
+ *
+ *
refine
Boolean
+ * 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.
+ *
+ *
transform
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.
+ * 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.
+ *
+ *
validate
Function, 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.
+ *
+ *
dereference
Boolean
+ * 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.
+ *
+ *
deferredInit
Boolean
+ * 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:
+ *
+ *
+ *
Name
Type
Description
+ *
group
String[]
+ * A list of property names which should be set using the property group.
+ *
+ *
mode
String
+ * If mode is set to "shorthand", the properties can be set using a CSS like shorthand mode.
+ *
+ *
themeable
Boolean
+ * 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
+ *
classname
The fully-qualified name of the class (e.g. "qx.ui.core.Widget").
+ *
basename
The namespace part of the class name (e.g. "qx.ui.core").
+ *
constructor
A reference to the constructor of the class.
+ *
superclass
A 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}):
+ *
+ *
+ * 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.
+ *
+ *
+ * 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:
+ *
+ *
+ * @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:
+ *
+ *
Name
Type
Description
+ *
type
String
+ * Type of the class. Valid types are "abstract", "static" and "singleton".
+ * If unset it defaults to a regular non-static class.
+ *
+ *
extend
Class
The super class the current class inherits from.
+ *
implement
Interface | Interface[]
Single interface or array of interfaces the class implements.
+ *
include
Mixin | Mixin[]
Single mixin or array of mixins, which will be merged into the class.
+ *
construct
Function
The constructor of the class.
+ *
statics
Map
Map of static members of the class.
+ *
properties
Map
Map of property definitions. For a description of the format of a property definition see
+ * {@link qx.core.Property}.
+ *
members
Map
Map of instance members of the class.
+ *
environment
Map
Map of environment settings for this class. For a description of the format of a setting see
+ * {@link qx.core.Environment}.
+ *
events
Map
+ * Map of events the class fires. The keys are the names of the events and the values are the
+ * corresponding event type class names.
+ *
+ *
defer
Function
Function that is called at the end of processing the class declaration. It allows access to the declared statics, members and properties.
+ *
destruct
Function
The 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:
+ *
+ * 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.
+ *
+ *
The data to convert
+ *
The corresponding model object, which is only set in case of the use of an controller.
+ *
The source object for the binding
+ *
The target object.
+ *
+ * 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.
+ *
+ *
The source object
+ *
The target object
+ *
The data
+ *
+ * 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 "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:
+ *