text: define([
  	"dojo/_base/array",
  	"dojo/_base/lang",
  	"dojo/_base/declare",
  	"dojo/has",
  	"dojo/Stateful",
  	"./resolve",
  	"./sync"
  ], function(array, lang, declare, has, Stateful, resolve, sync){
  	if(has("mvc-bindings-log-api")){
  		function getLogContent(/*dojo/Stateful*/ target, /*String*/ targetProp){
  			return [target._setIdAttr || !target.declaredClass ? target : target.declaredClass, targetProp].join(":");
  		}
  
  		function logResolveFailure(target, targetProp){
  			console.warn(targetProp + " could not be resolved" + (typeof target == "string" ? (" with " + target) : "") + ".");
  		}
  	}
  
  	function getParent(/*dijit/_WidgetBase*/ w){
  		// summary:
  		//		Returns parent widget having data binding target for relative data binding.
  		// w: dijit/_WidgetBase
  		//		The widget.
  
  		// Usage of dijit/registry module is optional. Return null if it's not already loaded.
  		var registry;
  		try{
  			registry = require("dijit/registry");
  		}catch(e){
  			return;
  		}
  		var pn = w.domNode && w.domNode.parentNode, pw, pb;
  		while(pn){
  			pw = registry.getEnclosingWidget(pn);
  			if(pw){
  				var relTargetProp = pw._relTargetProp || "target", pt = lang.isFunction(pw.get) ? pw.get(relTargetProp) : pw[relTargetProp];
  				if(pt || relTargetProp in pw.constructor.prototype){
  					return pw; // dijit/_WidgetBase
  				}
  			}
  			pn = pw && pw.domNode.parentNode;
  		}
  	}
  
  	function bind(/*dojo/Stateful|String*/ source, /*String*/ sourceProp, /*dijit/_WidgetBase*/ target, /*String*/ targetProp, /*dojox/mvc/sync.options*/ options){
  		// summary:
  		//		Resolves the data binding literal, and starts data binding.
  		// source: dojo/Stateful|String
  		//		Source data binding literal or dojo/Stateful to be synchronized.
  		// sourceProp: String
  		//		The property name in source to be synchronized.
  		// target: dijit/_WidgetBase
  		//		Target dojo/Stateful to be synchronized.
  		// targetProp: String
  		//		The property name in target to be synchronized.
  		// options: dojox/mvc/sync.options
  		//		Data binding options.
  
  		var _handles = {}, parent = getParent(target), relTargetProp = parent && parent._relTargetProp || "target";
  
  		function resolveAndBind(){
  			_handles["Two"] && _handles["Two"].unwatch();
  			delete _handles["Two"];
  
  			var relTarget = parent && (lang.isFunction(parent.get) ? parent.get(relTargetProp) : parent[relTargetProp]),
  			 resolvedSource = resolve(source, relTarget),
  			 resolvedTarget = resolve(target, relTarget);
  
  			if(has("mvc-bindings-log-api") && (!resolvedSource || /^rel:/.test(source) && !parent)){ logResolveFailure(source, sourceProp); }
  			if(has("mvc-bindings-log-api") && (!resolvedTarget || /^rel:/.test(target) && !parent)){ logResolveFailure(target, targetProp); }
  			if(!resolvedSource || !resolvedTarget || (/^rel:/.test(source) || /^rel:/.test(target)) && !parent){ return; }
  			if((!resolvedSource.set || !resolvedSource.watch) && sourceProp == "*"){
  				if(has("mvc-bindings-log-api")){ logResolveFailure(source, sourceProp); }
  				return;
  			}
  
  			if(sourceProp == null){
  				// If source property is not specified, it means this handle is just for resolving data binding target.
  				// (For dojox/mvc/Group and dojox/mvc/Repeat)
  				// Do not perform data binding synchronization in such case.
  				lang.isFunction(resolvedTarget.set) ? resolvedTarget.set(targetProp, resolvedSource) : (resolvedTarget[targetProp] = resolvedSource);
  				if(has("mvc-bindings-log-api")){
  					console.log("dojox/mvc/_atBindingMixin set " + resolvedSource + " to: " + getLogContent(resolvedTarget, targetProp));
  				}
  			}else{
  				// Start data binding
  				_handles["Two"] = sync(resolvedSource, sourceProp, resolvedTarget, targetProp, options); // dojox/mvc/sync.handle
  			}
  		}
  
  		resolveAndBind();
  		if(parent && /^rel:/.test(source) || /^rel:/.test(target) && lang.isFunction(parent.set) && lang.isFunction(parent.watch)){
  			_handles["rel"] = parent.watch(relTargetProp, function(name, old, current){
  				if(old !== current){
  					if(has("mvc-bindings-log-api")){ console.log("Change in relative data binding target: " + parent); }
  					resolveAndBind();
  				}
  			});
  		}
  		var h = {};
  		h.unwatch = h.remove = function(){
  			for(var s in _handles){
  				_handles[s] && _handles[s].unwatch();
  				delete _handles[s];
  			}
  		};
  		return h;
  	}
  
  	var mixin = {
  		// summary:
  		//		The mixin for dijit/_WidgetBase to support data binding.
  
  		// dataBindAttr: String
  		//		The attribute name for data binding.
  		dataBindAttr: "data-mvc-bindings",
  
  		_dbpostscript: function(/*Object?*/ params, /*DomNode|String*/ srcNodeRef){
  			// summary:
  			//		See if any parameters for this widget are dojox/mvc/at handles.
  			//		If so, move them under this._refs to prevent widget implementations from referring them.
  
  			var refs = this._refs = (params || {}).refs || {};
  			for(var prop in params){
  				if((params[prop] || {}).atsignature == "dojox.mvc.at"){
  					var h = params[prop];
  					delete params[prop];
  					refs[prop] = h;
  				}
  			}
  
  			var dbParams = new Stateful(),
  			 _self = this;
  			dbParams.toString = function(){ return '[Mixin value of widget ' + _self.declaredClass + ', ' + (_self.id || 'NO ID') + ']'; };
  			dbParams.canConvertToLoggable = true;
  			this._startAtWatchHandles(dbParams);
  			for(var prop in refs){
  				if(dbParams[prop] !== void 0){
  					(params = params || {})[prop] = dbParams[prop];
  				}
  			}
  			this._stopAtWatchHandles();
  		},
  
  		_startAtWatchHandles: function(/*dojo/Stateful*/ bindWith){
  			// summary:
  			//		Establish data bindings based on dojox/mvc/at handles.
  			// bindWith: dojo/Stateful
  			//		The dojo/Stateful to bind properties with.
  
  			this.canConvertToLoggable = true;
  
  			var refs = this._refs;
  			if(refs){
  				var atWatchHandles = this._atWatchHandles = this._atWatchHandles || {};
  
  				// Clear the cache of properties that data binding is established with
  				this._excludes = null;
  
  				// First, establish non-wildcard data bindings
  				for(var prop in refs){
  					if(!refs[prop] || prop == "*"){ continue; }
  					atWatchHandles[prop] = bind(refs[prop].target, refs[prop].targetProp, bindWith || this, prop, {bindDirection: refs[prop].bindDirection, converter: refs[prop].converter, equals: refs[prop].equalsCallback});
  				}
  
  				// Then establish wildcard data bindings
  				if((refs["*"] || {}).atsignature == "dojox.mvc.at"){
  					atWatchHandles["*"] = bind(refs["*"].target, refs["*"].targetProp, bindWith || this, "*", {bindDirection: refs["*"].bindDirection, converter: refs["*"].converter, equals: refs["*"].equalsCallback});
  				}
  			}
  		},
  
  		_stopAtWatchHandles: function(){
  			// summary:
  			//		Stops data binding synchronization handles as widget is destroyed.
  
  			for(var s in this._atWatchHandles){
  				this._atWatchHandles[s].unwatch();
  				delete this._atWatchHandles[s];
  			}
  		},
  
  		_setAtWatchHandle: function(/*String*/ name, /*Anything*/ value){
  			// summary:
  			//		Called if the value is a dojox/mvc/at handle.
  			//		If this widget has started, start data binding with the new dojox/mvc/at handle.
  			//		Otherwise, queue it up to this._refs so that _dbstartup() can pick it up.
  
  			if(name == "ref"){
  				throw new Error(this + ": 1.7 ref syntax used in conjunction with 1.8 dojox/mvc/at syntax, which is not supported.");
  			}
  
  			// Claen up older data binding
  			var atWatchHandles = this._atWatchHandles = this._atWatchHandles || {};
  			if(atWatchHandles[name]){
  				atWatchHandles[name].unwatch();
  				delete atWatchHandles[name];
  			}
  
  			// Claar the value
  			this[name] = null;
  
  			// Clear the cache of properties that data binding is established with
  			this._excludes = null;
  
  			if(this._started){
  				// If this widget has been started already, establish data binding immediately.
  				atWatchHandles[name] = bind(value.target, value.targetProp, this, name, {bindDirection: value.bindDirection, converter: value.converter, equals: value.equalsCallback});
  			}else{
  				// Otherwise, queue it up to this._refs so that _dbstartup() can pick it up.
  				this._refs[name] = value;
  			}
  		},
  
  		_setBind: function(/*Object*/ value){
  			// summary:
  			//		Sets data binding described in data-mvc-bindings.
  
  			var list = eval("({" + value + "})");
  			for(var prop in list){
  				var h = list[prop];
  				if((h || {}).atsignature != "dojox.mvc.at"){
  					console.warn(prop + " in " + dataBindAttr + " is not a data binding handle.");
  				}else{
  					this._setAtWatchHandle(prop, h);
  				}
  			}
  		},
  
  		_getExcludesAttr: function(){
  			// summary:
  			//		Returns list of all properties that data binding is established with.
  
  			if(this._excludes){ 
  				return this._excludes;  // String[] 
  			}
  			var list = [];
  			for(var s in this._atWatchHandles){
  				if(s != "*"){ list.push(s); }
  			}
  			return list; // String[]
  		},
  
  		_getPropertiesAttr: function(){
  			// summary:
  			//		Returns list of all properties in this widget, except "id".
  			// returns: String[]
  			//		 The list of all properties in this widget, except "id"..
  
  			if(this.constructor._attribs){
  				return this.constructor._attribs; // String[]
  			}
  			var list = ["onClick"].concat(this.constructor._setterAttrs);
  			array.forEach(["id", "excludes", "properties", "ref", "binding"], function(s){
  				var index = array.indexOf(list, s);
  				if(index >= 0){ list.splice(index, 1); }
  			});
  			return this.constructor._attribs = list; // String[]
  		}
  	};
  
  	mixin[mixin.dataBindAttr] = ""; // Let parser treat the attribute as string
  
  	var _atBindingMixin = declare("dojox/mvc/_atBindingMixin", null, mixin);
  	_atBindingMixin.mixin = mixin; // Keep the plain object version
  	return _atBindingMixin;
  });
  