'use strict';

var aFrom               = require('es5-ext/array/from')
  , isFunction          = require('es5-ext/function/is-function')
  , isPlainObject       = require('es5-ext/object/is-plain-object')
  , iterable            = require('es5-ext/iterable/validate-object')
  , isMap               = require('es6-map/is-map')
  , isObservable        = require('observable-value/is-observable')
  , isObservableValue   = require('observable-value/is-observable-value')
  , makeElement         = require('dom-ext/document/#/make-element')
  , normalize           = require('dom-ext/document/#/normalize')
  , castAttributes      = require('dom-ext/element/#/cast-attributes')
  , elExtend            = require('dom-ext/element/#/extend')
  , remove              = require('dom-ext/element/#/remove')
  , replaceContent      = require('dom-ext/element/#/replace-content')
  , isNode              = require('dom-ext/node/is-node')
  , memoize             = require('memoizee/plain')
  , getOneArgNormalizer = require('memoizee/normalizers/get-1')
  , getNormalizer       = require('memoizee/normalizers/get-fixed');

module.exports = function (childName, isChildNode) {
	return function (listArg/*, renderItem, thisArg*/) {
		var attrs, renderItem, render, thisArg, cb, onEmpty, listValue = listArg, list;
		if (isPlainObject(listValue) && !isFunction(arguments[1])) {
			attrs = listValue;
			listValue = arguments[1];
			renderItem = arguments[2];
			thisArg = arguments[3];
		} else {
			renderItem = arguments[1];
			thisArg = arguments[2];
		}
		if (isNode(listValue) || !isFunction(renderItem)) return elExtend.apply(this, arguments);
		iterable(listValue);
		if (attrs) {
			if (attrs.onEmpty) {
				onEmpty = attrs.onEmpty;
				delete attrs.onEmpty;
				if (isNode(onEmpty)) remove.call(onEmpty);
			}
			castAttributes.call(this, attrs);
		}
		cb = function (item, index, list) {
			var result;
			result = this.safeCollectRaw(renderItem.bind(thisArg, item, index, list));
			if (result == null) return null;
			if (isObservableValue(result)) {
				return normalize.call(this.document, result.map(function (result) {
					result = normalize.call(this.document, result);
					if (result == null) return null;
					if (!isChildNode(result)) result = makeElement.call(this.document, childName, result);
					return result;
				}, this));
			}
			result = normalize.call(this.document, result);
			if (!isChildNode(result)) result = makeElement.call(this.document, childName, result);
			return result;
		};
		render = function () {
			var content;
			content = aFrom(list, cb, this.domjs);
			if (!content.length && onEmpty) content = onEmpty;
			replaceContent.call(this, content);
		}.bind(this);
		list = listValue;
		if (isObservable(list)) {
			cb = memoize(cb, { normalizer: isMap(list) ? getNormalizer(2) : getOneArgNormalizer() });
			list.on('change', render);
		}
		render();
		return this;
	};
};

@@ -1294,18 +1294,34 @@
 rg, list
+%0A%09%09  , onNewList
 ;%0A
-
 %09%09if (is
@@ -1649,16 +1649,51 @@
 nts);%0A%09%09
+if (!isObservableValue(listValue)) 
 iterable
@@ -2607,16 +2607,23 @@
 ontent =
+ list ?
  aFrom(l
@@ -2642,16 +2642,21 @@
 s.domjs)
+ : %5B%5D
 ;%0A%09%09%09if 
@@ -2764,26 +2764,97 @@
 ;%0A%09%09
-list = listValue
+onNewList = function (newList) %7B%0A%09%09%09if (isObservable(list)) list.off('change', render)
 ;%0A
+%09
 %09%09if
@@ -2872,17 +2872,20 @@
 ble(
-l
+newL
 ist)) %7B%0A
 %09%09%09c
@@ -2880,16 +2880,17 @@
 ist)) %7B%0A
+%09
 %09%09%09cb = 
@@ -2921,17 +2921,20 @@
 : isMap(
-l
+newL
 ist) ? g
@@ -2980,17 +2980,21 @@
  %7D);%0A%09%09%09
-l
+%09newL
 ist.on('
@@ -3015,23 +3015,234 @@
 r);%0A
+%09
 %09%09%7D%0A%09%09
-render();
+%09list = newList;%0A%09%09%09render();%0A%09%09%7D;%0A%09%09if (isObservableValue(listValue)) %7B%0A%09%09%09onNewList(listValue.value);%0A%09%09%09listValue.on('change', function (event) %7B onNewList(event.newValue); %7D);%0A%09%09%7D else %7B%0A%09%09%09onNewList(listValue);%0A%09%09%7D
 %0A%09%09r

Support observable value as list input