@qooxdoo/framework
Version:
The JS Framework for Coders
350 lines (291 loc) • 10.6 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2013 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Martin Wittemann (wittemann)
* Daniel Wagner (danielwagner)
************************************************************************ */
/**
* This is the base collection for all widgets and offers a good foundation
* for all widgets including enabled state handling, config and template storage
* and event handling to name a few key features.
*
* @require(qx.module.Dataset)
* @require(qx.module.util.String)
* @require(qx.module.event.Native)
*
* @group (Widget)
*/
qx.Bootstrap.define("qx.ui.website.Widget", {
extend : qxWeb,
implement: [ qx.core.IDisposable ],
statics : {
/**
* Factory method for the widget which converts a standard
* collection into a collection of widgets.
*
* @return {qx.ui.website.Widget} A widget.
*
* @attach {qxWeb}
*/
widget : function() {
var widget = new qx.ui.website.Widget(this);
widget.init();
return widget;
},
/**
* Creates a new collection from the given argument. This can either be an
* HTML string, a single DOM element or an array of elements
*
* @param html {String|Element[]} HTML string or DOM element(s)
* @return {qxWeb} Collection of elements
*/
create : function(html) {
return new qx.ui.website.Widget(qxWeb.create(html));
},
/**
* Fetches elements with a data attribute named <code>data-qx-class</code>
* containing the class name of the desired widget and initializes them as
* widgets.
*
* @param selector {String?} Optional selector expression or filter function to
* restrict the list of elements
* @attachStatic {qxWeb}
*/
initWidgets : function(selector) {
var elements = qxWeb("*[data-qx-class]");
if (selector) {
elements = elements.filter(selector);
}
elements._forEachElementWrapped(function(widget) {
widget.init();
});
},
/**
* Returns a wrapper Array that maps the widget API available on
* the first item in the current collection to all items in the
* collection.
*
* @attach {qxWeb}
* @return {qxWeb[]} Collection of widgets
*/
toWidgetCollection: function() {
var args = this.toArray().map(function(el) { return qxWeb(el); });
// Set the context for the 'bind' call (will be replaced by new)
Array.prototype.unshift.call(args, null);
// Create temporary constructor with bound arguments
var Temp = qx.core.Wrapper.bind.apply(qx.core.Wrapper, args);
return new Temp();
}
},
construct : function(selector, context) {
var col = this.base(arguments, selector, context);
if (col.length > 1) {
throw new Error("The collection must not contain more than one element.");
}
Array.prototype.push.apply(this, Array.prototype.slice.call(col, 0, col.length));
},
members : {
__cssPrefix : null,
/**
* Responsible for initializing of the widget. This checks for the data attribute
* named <code>data-qx-class</code> and initializes the widget if necessary.
* @return {Boolean} <code>true</code> if the widget has been initialized
*/
init : function() {
if (this.getProperty("$$qx-widget-initialized")) {
return false;
}
this.setAttribute("data-qx-class", this.classname);
this.addClass("qx-widget");
this.addClass(this.getCssPrefix());
this.setProperty("$$qx-widget-initialized", true);
if(this[0]) {
this[0].$widget = this;
}
return true;
},
/**
* Return the proper CSS prefix for the current widget. This prefix is
* based on the current classname.
*
* @return {String} The CSS prefix for the current object.
*/
getCssPrefix : function() {
if (!this.__cssPrefix) {
var split = this.classname.split(".");
this.__cssPrefix = "qx-" + split[split.length - 1].toLowerCase();
}
return this.__cssPrefix;
},
/**
* Changes the enabled state of the current collection, which means all
* widgets in the collection. This sets the disabled attribute on the
* elements and all its children.
*
* @param value {Boolean} The enabled value.
* @return {qx.ui.website.Widget} The collection for chaining
*/
setEnabled : function(value) {
this.setAttribute("disabled", !value);
this.find("*").setAttribute("disabled", !value);
return this;
},
/**
* Returns weather the first widget in the collection is enabled or not.
*
* @return {Boolean} The enabled state of the collection.
*/
getEnabled : function() {
return !this.getAttribute("disabled");
},
/**
* Setter for the widget-specific templates. The available templates can
* be found in the documentation of the corresponding widget. The templates
* will be rendered using
* <a href='https://github.com/janl/mustache.js/'>mustache.js</a>.
*
* Please keep in mind to call {@link #render} after you change any
* template or config setting.
*
* @param name {String} The name of the template.
* @param template {String} The template string.
*
* @return {qx.ui.website.Widget} The widget instance for chaining.
*/
setTemplate : function(name, template) {
return this._setData("templates", name, template);
},
/**
* Setter for the widget-specific config. The available config settings can
* be found in the documentation of the corresponding widget. Each config
* setting can be set in the markup as data-attribute, prefixed with
* <code>data-qx</code> e.g. <code>data-qx-length="5"</code>.
*
* Please keep in mind to call {@link #render} after you change any
* template or config setting.
*
* @param name {String} The name of the config setting.
* @param config {var} The value of the config setting.
* @return {qx.ui.website.Widget} The widget instance for chaining.
*/
setConfig : function(name, config) {
return this._setData("config", name, config);
},
/**
* Helper to set either config or template values.
*
* @param type {String} Either <code>templates</code> or <code>config</code>.
* @param name {String} The name for the value to store.
* @param data {var} The data to store.
* @return {qx.ui.website.Widget} The widget instance for chaining.
*/
_setData : function(type, name, data) {
if (!this["$$storage_" + type]) {
this["$$storage_" + type] = {};
}
this["$$storage_" + type][name] = data;
return this;
},
/**
* Returns the used template. This includes custom templates
* as the default templates defined by the widgets.
*
* @param name {String} The name of the template.
* @return {String} The template string or <code>undefined</code>.
*/
getTemplate : function(name) {
return this._getData("templates", name);
},
/**
* Returns the config setting currently in use for the given widget.
* This can either be the user set config value, the value set in
* the markup via data-attribute, the widgets default config value or
* <code>undefined</code>, if non is set.
*
* @param name {String} The name of the config.
* @return {var} The value of the config or <code>undefined</code>.
*/
getConfig : function(name) {
return this._getData("config", name);
},
/**
* Internal helper for querying the values for templates and configs. In the
* case of a config value, the method also reads the corresponding data-attribute
* for possible values.
*
* @param type {String} Either <code>templates</code> or <code>config</code>.
* @param name {String} The name for the value to fetch.
* @return {var} The value store for the given arguments.
*/
_getData : function(type, name) {
var storage = this["$$storage_" + type];
var item;
if (storage) {
item = storage[name];
}
if (item === undefined && type == "config") {
var attribName = "qx" + qxWeb.string.firstUp(type) +
qxWeb.string.firstUp(name);
item = this.getData(attribName);
// not defined HTML attributes result in 'null' values
if (!this[0] || (!this[0].dataset && item === null)) {
item = undefined;
}
try {
item = JSON.parse(item);
} catch(e) {}
}
if (item === undefined && this.constructor["_" + type]) {
return this.constructor["_" + type][name];
}
return item;
},
/**
* The render method is responsible for applying changed config
* and template settings. This method is usually overridden and specified
* by the subclassing widgets like the slider or tabs.
*
* @return {qx.ui.website.Widget} The widget collection for chaining.
*/
render : function() {
// empty method
return this;
},
/**
* Dispose the widget, making sure all objects are ready for
* garbage collection. This mainly means deleting connections to the
* DOM including event listeners.
* @return {qxWeb} Plain qxWeb collection
*/
dispose : function() {
this.removeAttribute("data-qx-class");
this.setProperty("config", undefined);
this.setProperty("templates", undefined);
var contextProperty = this.classname.replace(/\./g, "-") + "-context";
this.setProperty(contextProperty, undefined);
this.setProperty("$$qx-widget-initialized", undefined);
this.removeClass("qx-widget");
this.removeClass(this.getCssPrefix());
for (var name in this.constructor.$$events) {
this.allOff(name);
}
this[0].$widget = null;
return qxWeb.$init(this, qxWeb);
}
},
defer : function(statics) {
qxWeb.$attach({
widget: statics.widget,
toWidgetCollection : statics.toWidgetCollection
});
qxWeb.$attachStatic({
initWidgets : statics.initWidgets
});
}
});