/**
* Adds HTML Parser support to the base Widget class
*
* @module widget
* @submodule widget-htmlparser
* @for Widget
*/
var Widget = Y.Widget,
Node = Y.Node,
Lang = Y.Lang,
SRC_NODE = "srcNode",
CONTENT_BOX = "contentBox";
/**
* Object hash, defining how attribute values are to be parsed from
* markup contained in the widget's content box. e.g.:
* <pre>
* {
* // Set single Node references using selector syntax
* // (selector is run through node.one)
* titleNode: "span.yui-title",
* // Set NodeList references using selector syntax
* // (array indicates selector is to be run through node.all)
* listNodes: ["li.yui-item"],
* // Set other attribute types, using a parse function.
* // Context is set to the widget instance.
* label: function(contentBox) {
* return contentBox.one("span.title").get("innerHTML");
* }
* }
* </pre>
*
* @property HTML_PARSER
* @type Object
* @static
*/
Widget.HTML_PARSER = {};
/**
* The build configuration for the Widget class.
* <p>
* Defines the static fields which need to be aggregated,
* when this class is used as the main class passed to
* the <a href="Base.html#method_build">Base.build</a> method.
* </p>
* @property _buildCfg
* @type Object
* @static
* @final
* @private
*/
Widget._buildCfg = {
aggregates : ["HTML_PARSER"]
};
/**
* The DOM node to parse for configuration values, passed to the Widget's HTML_PARSER definition
*
* @attribute srcNode
* @type String | Node
* @writeOnce
*/
Widget.ATTRS[SRC_NODE] = {
value: null,
setter: Node.one,
getter: "_getSrcNode",
writeOnce: true
};
Y.mix(Widget.prototype, {
/**
* @method _getSrcNode
* @protected
* @return {Node} The Node to apply HTML_PARSER to
*/
_getSrcNode : function(val) {
return val || this.get(CONTENT_BOX);
},
/**
* Implement the BaseCore _preAddAttrs method hook, to add
* the srcNode and related attributes, so that HTML_PARSER
* (which relies on `this.get("srcNode")`) can merge in it's
* results before the rest of the attributes are added.
*
* @method _preAddAttrs
* @protected
*
* @param attrs {Object} The full hash of statically defined ATTRS
* attributes being added for this instance
*
* @param userVals {Object} The hash of user values passed to
* the constructor
*
* @param lazy {boolean} Whether or not to add the attributes lazily
*/
_preAddAttrs : function(attrs, userVals, lazy) {
var preAttrs = {
id : attrs.id,
boundingBox : attrs.boundingBox,
contentBox : attrs.contentBox,
srcNode : attrs.srcNode || Y.Object(Widget.ATTRS[SRC_NODE])
};
this.addAttrs(preAttrs, userVals, lazy);
delete attrs.boundingBox;
delete attrs.contentBox;
delete attrs.srcNode;
delete attrs.id;
if (this._applyParser) {
this._applyParser(userVals);
}
},
/**
* @method _applyParsedConfig
* @protected
* @return {Object} The merged configuration literal
*/
_applyParsedConfig : function(node, cfg, parsedCfg) {
return (parsedCfg) ? Y.mix(cfg, parsedCfg, false) : cfg;
},
/**
* Utility method used to apply the <code>HTML_PARSER</code> configuration for the
* instance, to retrieve config data values.
*
* @method _applyParser
* @protected
* @param config {Object} User configuration object (will be populated with values from Node)
*/
_applyParser : function(config) {
var widget = this,
srcNode = this._getNodeToParse(),
schema = widget._getHtmlParser(),
parsedConfig,
val;
if (schema && srcNode) {
Y.Object.each(schema, function(v, k, o) {
val = null;
if (Lang.isFunction(v)) {
val = v.call(widget, srcNode);
} else {
if (Lang.isArray(v)) {
val = srcNode.all(v[0]);
if (val.isEmpty()) {
val = null;
}
} else {
val = srcNode.one(v);
}
}
if (val !== null && val !== undefined) {
parsedConfig = parsedConfig || {};
parsedConfig[k] = val;
}
});
}
config = widget._applyParsedConfig(srcNode, config, parsedConfig);
},
/**
* Determines whether we have a node reference which we should try and parse.
*
* The current implementation does not parse nodes generated from CONTENT_TEMPLATE,
* only explicitly set srcNode, or contentBox attributes.
*
* @method _getNodeToParse
* @return {Node} The node reference to apply HTML_PARSER to.
* @private
*/
_getNodeToParse : function() {
var srcNode = this.get("srcNode");
return (!this._cbFromTemplate) ? srcNode : null;
},
/**
* Gets the HTML_PARSER definition for this instance, by merging HTML_PARSER
* definitions across the class hierarchy.
*
* @private
* @method _getHtmlParser
* @return {Object} HTML_PARSER definition for this instance
*/
_getHtmlParser : function() {
// Removed caching for kweight. This is a private method
// and only called once so don't need to cache HTML_PARSER
var classes = this._getClasses(),
parser = {},
i, p;
for (i = classes.length - 1; i >= 0; i--) {
p = classes[i].HTML_PARSER;
if (p) {
Y.mix(parser, p, true);
}
}
return parser;
}
});