/* ---- lib/Class.coffee ---- */


(function() {
  var Class,
    slice = [].slice;

  Class = (function() {
    function Class() {}

    Class.prototype.trace = true;

    Class.prototype.log = function() {
      var args;
      args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
      if (!this.trace) {
        return;
      }
      if (typeof console === 'undefined') {
        return;
      }
      args.unshift("[" + this.constructor.name + "]");
      console.log.apply(console, args);
      return this;
    };

    Class.prototype.logStart = function() {
      var args, name;
      name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
      if (!this.trace) {
        return;
      }
      this.logtimers || (this.logtimers = {});
      this.logtimers[name] = +(new Date);
      if (args.length > 0) {
        this.log.apply(this, ["" + name].concat(slice.call(args), ["(started)"]));
      }
      return this;
    };

    Class.prototype.logEnd = function() {
      var args, ms, name;
      name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
      ms = +(new Date) - this.logtimers[name];
      this.log.apply(this, ["" + name].concat(slice.call(args), ["(Done in " + ms + "ms)"]));
      return this;
    };

    return Class;

  })();

  window.Class = Class;

}).call(this);

/* ---- lib/Promise.coffee ---- */


(function() {
  var Promise,
    slice = [].slice;

  Promise = (function() {
    Promise.when = function() {
      var args, fn, i, len, num_uncompleted, promise, task, task_id, tasks;
      tasks = 1 <= arguments.length ? slice.call(arguments, 0) : [];
      num_uncompleted = tasks.length;
      args = new Array(num_uncompleted);
      promise = new Promise();
      fn = function(task_id) {
        return task.then(function() {
          args[task_id] = Array.prototype.slice.call(arguments);
          num_uncompleted--;
          if (num_uncompleted === 0) {
            return promise.complete.apply(promise, args);
          }
        });
      };
      for (task_id = i = 0, len = tasks.length; i < len; task_id = ++i) {
        task = tasks[task_id];
        fn(task_id);
      }
      return promise;
    };

    function Promise() {
      this.resolved = false;
      this.end_promise = null;
      this.result = null;
      this.callbacks = [];
    }

    Promise.prototype.resolve = function() {
      var back, callback, i, len, ref;
      if (this.resolved) {
        return false;
      }
      this.resolved = true;
      this.data = arguments;
      if (!arguments.length) {
        this.data = [true];
      }
      this.result = this.data[0];
      ref = this.callbacks;
      for (i = 0, len = ref.length; i < len; i++) {
        callback = ref[i];
        back = callback.apply(callback, this.data);
      }
      if (this.end_promise) {
        return this.end_promise.resolve(back);
      }
    };

    Promise.prototype.fail = function() {
      return this.resolve(false);
    };

    Promise.prototype.then = function(callback) {
      if (this.resolved === true) {
        callback.apply(callback, this.data);
        return;
      }
      this.callbacks.push(callback);
      return this.end_promise = new Promise();
    };

    return Promise;

  })();

  window.Promise = Promise;


  /*
  s = Date.now()
  log = (text) ->
  	console.log Date.now()-s, Array.prototype.slice.call(arguments).join(", ")
  
  log "Started"
  
  cmd = (query) ->
  	p = new Promise()
  	setTimeout ( ->
  		p.resolve query+" Result"
  	), 100
  	return p
  
  back = cmd("SELECT * FROM message").then (res) ->
  	log res
  	return "Return from query"
  .then (res) ->
  	log "Back then", res
  
  log "Query started", back
   */

}).call(this);

/* ---- lib/Prototypes.coffee ---- */


(function() {
  String.prototype.startsWith = function(s) {
    return this.slice(0, s.length) === s;
  };

  String.prototype.endsWith = function(s) {
    return s === '' || this.slice(-s.length) === s;
  };

  String.prototype.repeat = function(count) {
    return new Array(count + 1).join(this);
  };

  window.isEmpty = function(obj) {
    var key;
    for (key in obj) {
      return false;
    }
    return true;
  };

}).call(this);

/* ---- lib/maquette.js ---- */


(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['exports'], factory);
    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
        // CommonJS
        factory(exports);
    } else {
        // Browser globals
        factory(root.maquette = {});
    }
}(this, function (exports) {
    'use strict';
    ;
    ;
    ;
    ;
    var NAMESPACE_W3 = 'http://www.w3.org/';
    var NAMESPACE_SVG = NAMESPACE_W3 + '2000/svg';
    var NAMESPACE_XLINK = NAMESPACE_W3 + '1999/xlink';
    // Utilities
    var emptyArray = [];
    var extend = function (base, overrides) {
        var result = {};
        Object.keys(base).forEach(function (key) {
            result[key] = base[key];
        });
        if (overrides) {
            Object.keys(overrides).forEach(function (key) {
                result[key] = overrides[key];
            });
        }
        return result;
    };
    // Hyperscript helper functions
    var same = function (vnode1, vnode2) {
        if (vnode1.vnodeSelector !== vnode2.vnodeSelector) {
            return false;
        }
        if (vnode1.properties && vnode2.properties) {
            if (vnode1.properties.key !== vnode2.properties.key) {
                return false;
            }
            return vnode1.properties.bind === vnode2.properties.bind;
        }
        return !vnode1.properties && !vnode2.properties;
    };
    var toTextVNode = function (data) {
        return {
            vnodeSelector: '',
            properties: undefined,
            children: undefined,
            text: data.toString(),
            domNode: null
        };
    };
    var appendChildren = function (parentSelector, insertions, main) {
        for (var i = 0; i < insertions.length; i++) {
            var item = insertions[i];
            if (Array.isArray(item)) {
                appendChildren(parentSelector, item, main);
            } else {
                if (item !== null && item !== undefined) {
                    if (!item.hasOwnProperty('vnodeSelector')) {
                        item = toTextVNode(item);
                    }
                    main.push(item);
                }
            }
        }
    };
    // Render helper functions
    var missingTransition = function () {
        throw new Error('Provide a transitions object to the projectionOptions to do animations');
    };
    var DEFAULT_PROJECTION_OPTIONS = {
        namespace: undefined,
        eventHandlerInterceptor: undefined,
        styleApplyer: function (domNode, styleName, value) {
            // Provides a hook to add vendor prefixes for browsers that still need it.
            domNode.style[styleName] = value;
        },
        transitions: {
            enter: missingTransition,
            exit: missingTransition
        }
    };
    var applyDefaultProjectionOptions = function (projectorOptions) {
        return extend(DEFAULT_PROJECTION_OPTIONS, projectorOptions);
    };
    var checkStyleValue = function (styleValue) {
        if (typeof styleValue !== 'string') {
            throw new Error('Style values must be strings');
        }
    };
    var setProperties = function (domNode, properties, projectionOptions) {
        if (!properties) {
            return;
        }
        var eventHandlerInterceptor = projectionOptions.eventHandlerInterceptor;
        var propNames = Object.keys(properties);
        var propCount = propNames.length;
        for (var i = 0; i < propCount; i++) {
            var propName = propNames[i];
            /* tslint:disable:no-var-keyword: edge case */
            var propValue = properties[propName];
            /* tslint:enable:no-var-keyword */
            if (propName === 'className') {
                throw new Error('Property "className" is not supported, use "class".');
            } else if (propName === 'class') {
                if (domNode.className) {
                    // May happen if classes is specified before class
                    domNode.className += ' ' + propValue;
                } else {
                    domNode.className = propValue;
                }
            } else if (propName === 'classes') {
                // object with string keys and boolean values
                var classNames = Object.keys(propValue);
                var classNameCount = classNames.length;
                for (var j = 0; j < classNameCount; j++) {
                    var className = classNames[j];
                    if (propValue[className]) {
                        domNode.classList.add(className);
                    }
                }
            } else if (propName === 'styles') {
                // object with string keys and string (!) values
                var styleNames = Object.keys(propValue);
                var styleCount = styleNames.length;
                for (var j = 0; j < styleCount; j++) {
                    var styleName = styleNames[j];
                    var styleValue = propValue[styleName];
                    if (styleValue) {
                        checkStyleValue(styleValue);
                        projectionOptions.styleApplyer(domNode, styleName, styleValue);
                    }
                }
            } else if (propName === 'key') {
                continue;
            } else if (propValue === null || propValue === undefined) {
                continue;
            } else {
                var type = typeof propValue;
                if (type === 'function') {
                    if (propName.lastIndexOf('on', 0) === 0) {
                        if (eventHandlerInterceptor) {
                            propValue = eventHandlerInterceptor(propName, propValue, domNode, properties);    // intercept eventhandlers
                        }
                        if (propName === 'oninput') {
                            (function () {
                                // record the evt.target.value, because IE and Edge sometimes do a requestAnimationFrame between changing value and running oninput
                                var oldPropValue = propValue;
                                propValue = function (evt) {
                                    evt.target['oninput-value'] = evt.target.value;
                                    // may be HTMLTextAreaElement as well
                                    oldPropValue.apply(this, [evt]);
                                };
                            }());
                        }
                        domNode[propName] = propValue;
                    }
                } else if (type === 'string' && propName !== 'value' && propName !== 'innerHTML') {
                    if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
                        domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
                    } else {
                        domNode.setAttribute(propName, propValue);
                    }
                } else {
                    domNode[propName] = propValue;
                }
            }
        }
    };
    var updateProperties = function (domNode, previousProperties, properties, projectionOptions) {
        if (!properties) {
            return;
        }
        var propertiesUpdated = false;
        var propNames = Object.keys(properties);
        var propCount = propNames.length;
        for (var i = 0; i < propCount; i++) {
            var propName = propNames[i];
            // assuming that properties will be nullified instead of missing is by design
            var propValue = properties[propName];
            var previousValue = previousProperties[propName];
            if (propName === 'class') {
                if (previousValue !== propValue) {
                    throw new Error('"class" property may not be updated. Use the "classes" property for conditional css classes.');
                }
            } else if (propName === 'classes') {
                var classList = domNode.classList;
                var classNames = Object.keys(propValue);
                var classNameCount = classNames.length;
                for (var j = 0; j < classNameCount; j++) {
                    var className = classNames[j];
                    var on = !!propValue[className];
                    var previousOn = !!previousValue[className];
                    if (on === previousOn) {
                        continue;
                    }
                    propertiesUpdated = true;
                    if (on) {
                        classList.add(className);
                    } else {
                        classList.remove(className);
                    }
                }
            } else if (propName === 'styles') {
                var styleNames = Object.keys(propValue);
                var styleCount = styleNames.length;
                for (var j = 0; j < styleCount; j++) {
                    var styleName = styleNames[j];
                    var newStyleValue = propValue[styleName];
                    var oldStyleValue = previousValue[styleName];
                    if (newStyleValue === oldStyleValue) {
                        continue;
                    }
                    propertiesUpdated = true;
                    if (newStyleValue) {
                        checkStyleValue(newStyleValue);
                        projectionOptions.styleApplyer(domNode, styleName, newStyleValue);
                    } else {
                        projectionOptions.styleApplyer(domNode, styleName, '');
                    }
                }
            } else {
                if (!propValue && typeof previousValue === 'string') {
                    propValue = '';
                }
                if (propName === 'value') {
                    if (domNode[propName] !== propValue && domNode['oninput-value'] !== propValue) {
                        domNode[propName] = propValue;
                        // Reset the value, even if the virtual DOM did not change
                        domNode['oninput-value'] = undefined;
                    }
                    // else do not update the domNode, otherwise the cursor position would be changed
                    if (propValue !== previousValue) {
                        propertiesUpdated = true;
                    }
                } else if (propValue !== previousValue) {
                    var type = typeof propValue;
                    if (type === 'function') {
                        throw new Error('Functions may not be updated on subsequent renders (property: ' + propName + '). Hint: declare event handler functions outside the render() function.');
                    }
                    if (type === 'string' && propName !== 'innerHTML') {
                        if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
                            domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
                        } else {
                            domNode.setAttribute(propName, propValue);
                        }
                    } else {
                        if (domNode[propName] !== propValue) {
                            domNode[propName] = propValue;
                        }
                    }
                    propertiesUpdated = true;
                }
            }
        }
        return propertiesUpdated;
    };
    var findIndexOfChild = function (children, sameAs, start) {
        if (sameAs.vnodeSelector !== '') {
            // Never scan for text-nodes
            for (var i = start; i < children.length; i++) {
                if (same(children[i], sameAs)) {
                    return i;
                }
            }
        }
        return -1;
    };
    var nodeAdded = function (vNode, transitions) {
        if (vNode.properties) {
            var enterAnimation = vNode.properties.enterAnimation;
            if (enterAnimation) {
                if (typeof enterAnimation === 'function') {
                    enterAnimation(vNode.domNode, vNode.properties);
                } else {
                    transitions.enter(vNode.domNode, vNode.properties, enterAnimation);
                }
            }
        }
    };
    var nodeToRemove = function (vNode, transitions) {
        var domNode = vNode.domNode;
        if (vNode.properties) {
            var exitAnimation = vNode.properties.exitAnimation;
            if (exitAnimation) {
                domNode.style.pointerEvents = 'none';
                var removeDomNode = function () {
                    if (domNode.parentNode) {
                        domNode.parentNode.removeChild(domNode);
                    }
                };
                if (typeof exitAnimation === 'function') {
                    exitAnimation(domNode, removeDomNode, vNode.properties);
                    return;
                } else {
                    transitions.exit(vNode.domNode, vNode.properties, exitAnimation, removeDomNode);
                    return;
                }
            }
        }
        if (domNode.parentNode) {
            domNode.parentNode.removeChild(domNode);
        }
    };
    var checkDistinguishable = function (childNodes, indexToCheck, parentVNode, operation) {
        var childNode = childNodes[indexToCheck];
        if (childNode.vnodeSelector === '') {
            return;    // Text nodes need not be distinguishable
        }
        var properties = childNode.properties;
        var key = properties ? properties.key === undefined ? properties.bind : properties.key : undefined;
        if (!key) {
            for (var i = 0; i < childNodes.length; i++) {
                if (i !== indexToCheck) {
                    var node = childNodes[i];
                    if (same(node, childNode)) {
                        if (operation === 'added') {
                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'added, but there is now more than one. You must add unique key properties to make them distinguishable.');
                        } else {
                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'removed, but there were more than one. You must add unique key properties to make them distinguishable.');
                        }
                    }
                }
            }
        }
    };
    var createDom;
    var updateDom;
    var updateChildren = function (vnode, domNode, oldChildren, newChildren, projectionOptions) {
        if (oldChildren === newChildren) {
            return false;
        }
        oldChildren = oldChildren || emptyArray;
        newChildren = newChildren || emptyArray;
        var oldChildrenLength = oldChildren.length;
        var newChildrenLength = newChildren.length;
        var transitions = projectionOptions.transitions;
        var oldIndex = 0;
        var newIndex = 0;
        var i;
        var textUpdated = false;
        while (newIndex < newChildrenLength) {
            var oldChild = oldIndex < oldChildrenLength ? oldChildren[oldIndex] : undefined;
            var newChild = newChildren[newIndex];
            if (oldChild !== undefined && same(oldChild, newChild)) {
                textUpdated = updateDom(oldChild, newChild, projectionOptions) || textUpdated;
                oldIndex++;
            } else {
                var findOldIndex = findIndexOfChild(oldChildren, newChild, oldIndex + 1);
                if (findOldIndex >= 0) {
                    // Remove preceding missing children
                    for (i = oldIndex; i < findOldIndex; i++) {
                        nodeToRemove(oldChildren[i], transitions);
                        checkDistinguishable(oldChildren, i, vnode, 'removed');
                    }
                    textUpdated = updateDom(oldChildren[findOldIndex], newChild, projectionOptions) || textUpdated;
                    oldIndex = findOldIndex + 1;
                } else {
                    // New child
                    createDom(newChild, domNode, oldIndex < oldChildrenLength ? oldChildren[oldIndex].domNode : undefined, projectionOptions);
                    nodeAdded(newChild, transitions);
                    checkDistinguishable(newChildren, newIndex, vnode, 'added');
                }
            }
            newIndex++;
        }
        if (oldChildrenLength > oldIndex) {
            // Remove child fragments
            for (i = oldIndex; i < oldChildrenLength; i++) {
                nodeToRemove(oldChildren[i], transitions);
                checkDistinguishable(oldChildren, i, vnode, 'removed');
            }
        }
        return textUpdated;
    };
    var addChildren = function (domNode, children, projectionOptions) {
        if (!children) {
            return;
        }
        for (var i = 0; i < children.length; i++) {
            createDom(children[i], domNode, undefined, projectionOptions);
        }
    };
    var initPropertiesAndChildren = function (domNode, vnode, projectionOptions) {
        addChildren(domNode, vnode.children, projectionOptions);
        // children before properties, needed for value property of <select>.
        if (vnode.text) {
            domNode.textContent = vnode.text;
        }
        setProperties(domNode, vnode.properties, projectionOptions);
        if (vnode.properties && vnode.properties.afterCreate) {
            vnode.properties.afterCreate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
        }
    };
    createDom = function (vnode, parentNode, insertBefore, projectionOptions) {
        var domNode, i, c, start = 0, type, found;
        var vnodeSelector = vnode.vnodeSelector;
        if (vnodeSelector === '') {
            domNode = vnode.domNode = document.createTextNode(vnode.text);
            if (insertBefore !== undefined) {
                parentNode.insertBefore(domNode, insertBefore);
            } else {
                parentNode.appendChild(domNode);
            }
        } else {
            for (i = 0; i <= vnodeSelector.length; ++i) {
                c = vnodeSelector.charAt(i);
                if (i === vnodeSelector.length || c === '.' || c === '#') {
                    type = vnodeSelector.charAt(start - 1);
                    found = vnodeSelector.slice(start, i);
                    if (type === '.') {
                        domNode.classList.add(found);
                    } else if (type === '#') {
                        domNode.id = found;
                    } else {
                        if (found === 'svg') {
                            projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
                        }
                        if (projectionOptions.namespace !== undefined) {
                            domNode = vnode.domNode = document.createElementNS(projectionOptions.namespace, found);
                        } else {
                            domNode = vnode.domNode = document.createElement(found);
                        }
                        if (insertBefore !== undefined) {
                            parentNode.insertBefore(domNode, insertBefore);
                        } else {
                            parentNode.appendChild(domNode);
                        }
                    }
                    start = i + 1;
                }
            }
            initPropertiesAndChildren(domNode, vnode, projectionOptions);
        }
    };
    updateDom = function (previous, vnode, projectionOptions) {
        var domNode = previous.domNode;
        var textUpdated = false;
        if (previous === vnode) {
            return false;    // By contract, VNode objects may not be modified anymore after passing them to maquette
        }
        var updated = false;
        if (vnode.vnodeSelector === '') {
            if (vnode.text !== previous.text) {
                var newVNode = document.createTextNode(vnode.text);
                domNode.parentNode.replaceChild(newVNode, domNode);
                vnode.domNode = newVNode;
                textUpdated = true;
                return textUpdated;
            }
        } else {
            if (vnode.vnodeSelector.lastIndexOf('svg', 0) === 0) {
                projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
            }
            if (previous.text !== vnode.text) {
                updated = true;
                if (vnode.text === undefined) {
                    domNode.removeChild(domNode.firstChild);    // the only textnode presumably
                } else {
                    domNode.textContent = vnode.text;
                }
            }
            updated = updateChildren(vnode, domNode, previous.children, vnode.children, projectionOptions) || updated;
            updated = updateProperties(domNode, previous.properties, vnode.properties, projectionOptions) || updated;
            if (vnode.properties && vnode.properties.afterUpdate) {
                vnode.properties.afterUpdate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
            }
        }
        if (updated && vnode.properties && vnode.properties.updateAnimation) {
            vnode.properties.updateAnimation(domNode, vnode.properties, previous.properties);
        }
        vnode.domNode = previous.domNode;
        return textUpdated;
    };
    var createProjection = function (vnode, projectionOptions) {
        return {
            update: function (updatedVnode) {
                if (vnode.vnodeSelector !== updatedVnode.vnodeSelector) {
                    throw new Error('The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)');
                }
                updateDom(vnode, updatedVnode, projectionOptions);
                vnode = updatedVnode;
            },
            domNode: vnode.domNode
        };
    };
    ;
    // The other two parameters are not added here, because the Typescript compiler creates surrogate code for desctructuring 'children'.
    exports.h = function (selector) {
        var properties = arguments[1];
        if (typeof selector !== 'string') {
            throw new Error();
        }
        var childIndex = 1;
        if (properties && !properties.hasOwnProperty('vnodeSelector') && !Array.isArray(properties) && typeof properties === 'object') {
            childIndex = 2;
        } else {
            // Optional properties argument was omitted
            properties = undefined;
        }
        var text = undefined;
        var children = undefined;
        var argsLength = arguments.length;
        // Recognize a common special case where there is only a single text node
        if (argsLength === childIndex + 1) {
            var onlyChild = arguments[childIndex];
            if (typeof onlyChild === 'string') {
                text = onlyChild;
            } else if (onlyChild !== undefined && onlyChild.length === 1 && typeof onlyChild[0] === 'string') {
                text = onlyChild[0];
            }
        }
        if (text === undefined) {
            children = [];
            for (; childIndex < arguments.length; childIndex++) {
                var child = arguments[childIndex];
                if (child === null || child === undefined) {
                    continue;
                } else if (Array.isArray(child)) {
                    appendChildren(selector, child, children);
                } else if (child.hasOwnProperty('vnodeSelector')) {
                    children.push(child);
                } else {
                    children.push(toTextVNode(child));
                }
            }
        }
        return {
            vnodeSelector: selector,
            properties: properties,
            children: children,
            text: text === '' ? undefined : text,
            domNode: null
        };
    };
    /**
 * Contains simple low-level utility functions to manipulate the real DOM.
 */
    exports.dom = {
        /**
     * Creates a real DOM tree from `vnode`. The [[Projection]] object returned will contain the resulting DOM Node in
     * its [[Projection.domNode|domNode]] property.
     * This is a low-level method. Users wil typically use a [[Projector]] instead.
     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
     * objects may only be rendered once.
     * @param projectionOptions - Options to be used to create and update the projection.
     * @returns The [[Projection]] which also contains the DOM Node that was created.
     */
        create: function (vnode, projectionOptions) {
            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
            createDom(vnode, document.createElement('div'), undefined, projectionOptions);
            return createProjection(vnode, projectionOptions);
        },
        /**
     * Appends a new childnode to the DOM which is generated from a [[VNode]].
     * This is a low-level method. Users wil typically use a [[Projector]] instead.
     * @param parentNode - The parent node for the new childNode.
     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
     * objects may only be rendered once.
     * @param projectionOptions - Options to be used to create and update the [[Projection]].
     * @returns The [[Projection]] that was created.
     */
        append: function (parentNode, vnode, projectionOptions) {
            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
            createDom(vnode, parentNode, undefined, projectionOptions);
            return createProjection(vnode, projectionOptions);
        },
        /**
     * Inserts a new DOM node which is generated from a [[VNode]].
     * This is a low-level method. Users wil typically use a [[Projector]] instead.
     * @param beforeNode - The node that the DOM Node is inserted before.
     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function.
     * NOTE: [[VNode]] objects may only be rendered once.
     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
     * @returns The [[Projection]] that was created.
     */
        insertBefore: function (beforeNode, vnode, projectionOptions) {
            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
            createDom(vnode, beforeNode.parentNode, beforeNode, projectionOptions);
            return createProjection(vnode, projectionOptions);
        },
        /**
     * Merges a new DOM node which is generated from a [[VNode]] with an existing DOM Node.
     * This means that the virtual DOM and the real DOM will have one overlapping element.
     * Therefore the selector for the root [[VNode]] will be ignored, but its properties and children will be applied to the Element provided.
     * This is a low-level method. Users wil typically use a [[Projector]] instead.
     * @param domNode - The existing element to adopt as the root of the new virtual DOM. Existing attributes and childnodes are preserved.
     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]] objects
     * may only be rendered once.
     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
     * @returns The [[Projection]] that was created.
     */
        merge: function (element, vnode, projectionOptions) {
            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
            vnode.domNode = element;
            initPropertiesAndChildren(element, vnode, projectionOptions);
            return createProjection(vnode, projectionOptions);
        }
    };
    /**
 * Creates a [[CalculationCache]] object, useful for caching [[VNode]] trees.
 * In practice, caching of [[VNode]] trees is not needed, because achieving 60 frames per second is almost never a problem.
 * For more information, see [[CalculationCache]].
 *
 * @param <Result> The type of the value that is cached.
 */
    exports.createCache = function () {
        var cachedInputs = undefined;
        var cachedOutcome = undefined;
        var result = {
            invalidate: function () {
                cachedOutcome = undefined;
                cachedInputs = undefined;
            },
            result: function (inputs, calculation) {
                if (cachedInputs) {
                    for (var i = 0; i < inputs.length; i++) {
                        if (cachedInputs[i] !== inputs[i]) {
                            cachedOutcome = undefined;
                        }
                    }
                }
                if (!cachedOutcome) {
                    cachedOutcome = calculation();
                    cachedInputs = inputs;
                }
                return cachedOutcome;
            }
        };
        return result;
    };
    /**
 * Creates a {@link Mapping} instance that keeps an array of result objects synchronized with an array of source objects.
 * See {@link http://maquettejs.org/docs/arrays.html|Working with arrays}.
 *
 * @param <Source>       The type of source items. A database-record for instance.
 * @param <Target>       The type of target items. A [[Component]] for instance.
 * @param getSourceKey   `function(source)` that must return a key to identify each source object. The result must either be a string or a number.
 * @param createResult   `function(source, index)` that must create a new result object from a given source. This function is identical
 *                       to the `callback` argument in `Array.map(callback)`.
 * @param updateResult   `function(source, target, index)` that updates a result to an updated source.
 */
    exports.createMapping = function (getSourceKey, createResult, updateResult) {
        var keys = [];
        var results = [];
        return {
            results: results,
            map: function (newSources) {
                var newKeys = newSources.map(getSourceKey);
                var oldTargets = results.slice();
                var oldIndex = 0;
                for (var i = 0; i < newSources.length; i++) {
                    var source = newSources[i];
                    var sourceKey = newKeys[i];
                    if (sourceKey === keys[oldIndex]) {
                        results[i] = oldTargets[oldIndex];
                        updateResult(source, oldTargets[oldIndex], i);
                        oldIndex++;
                    } else {
                        var found = false;
                        for (var j = 1; j < keys.length; j++) {
                            var searchIndex = (oldIndex + j) % keys.length;
                            if (keys[searchIndex] === sourceKey) {
                                results[i] = oldTargets[searchIndex];
                                updateResult(newSources[i], oldTargets[searchIndex], i);
                                oldIndex = searchIndex + 1;
                                found = true;
                                break;
                            }
                        }
                        if (!found) {
                            results[i] = createResult(source, i);
                        }
                    }
                }
                results.length = newSources.length;
                keys = newKeys;
            }
        };
    };
    /**
 * Creates a [[Projector]] instance using the provided projectionOptions.
 *
 * For more information, see [[Projector]].
 *
 * @param projectionOptions   Options that influence how the DOM is rendered and updated.
 */
    exports.createProjector = function (projectorOptions) {
        var projector;
        var projectionOptions = applyDefaultProjectionOptions(projectorOptions);
        projectionOptions.eventHandlerInterceptor = function (propertyName, eventHandler, domNode, properties) {
            return function () {
                // intercept function calls (event handlers) to do a render afterwards.
                projector.scheduleRender();
                return eventHandler.apply(properties.bind || this, arguments);
            };
        };
        var renderCompleted = true;
        var scheduled;
        var stopped = false;
        var projections = [];
        var renderFunctions = [];
        // matches the projections array
        var doRender = function () {
            scheduled = undefined;
            if (!renderCompleted) {
                return;    // The last render threw an error, it should be logged in the browser console.
            }
            renderCompleted = false;
            for (var i = 0; i < projections.length; i++) {
                var updatedVnode = renderFunctions[i]();
                projections[i].update(updatedVnode);
            }
            renderCompleted = true;
        };
        projector = {
            scheduleRender: function () {
                if (!scheduled && !stopped) {
                    scheduled = requestAnimationFrame(doRender);
                }
            },
            stop: function () {
                if (scheduled) {
                    cancelAnimationFrame(scheduled);
                    scheduled = undefined;
                }
                stopped = true;
            },
            resume: function () {
                stopped = false;
                renderCompleted = true;
                projector.scheduleRender();
            },
            append: function (parentNode, renderMaquetteFunction) {
                projections.push(exports.dom.append(parentNode, renderMaquetteFunction(), projectionOptions));
                renderFunctions.push(renderMaquetteFunction);
            },
            insertBefore: function (beforeNode, renderMaquetteFunction) {
                projections.push(exports.dom.insertBefore(beforeNode, renderMaquetteFunction(), projectionOptions));
                renderFunctions.push(renderMaquetteFunction);
            },
            merge: function (domNode, renderMaquetteFunction) {
                projections.push(exports.dom.merge(domNode, renderMaquetteFunction(), projectionOptions));
                renderFunctions.push(renderMaquetteFunction);
            },
            replace: function (domNode, renderMaquetteFunction) {
                var vnode = renderMaquetteFunction();
                createDom(vnode, domNode.parentNode, domNode, projectionOptions);
                domNode.parentNode.removeChild(domNode);
                projections.push(createProjection(vnode, projectionOptions));
                renderFunctions.push(renderMaquetteFunction);
            },
            detach: function (renderMaquetteFunction) {
                for (var i = 0; i < renderFunctions.length; i++) {
                    if (renderFunctions[i] === renderMaquetteFunction) {
                        renderFunctions.splice(i, 1);
                        return projections.splice(i, 1)[0];
                    }
                }
                throw new Error('renderMaquetteFunction was not found');
            }
        };
        return projector;
    };
}));


/* ---- utils/Animation.coffee ---- */


(function() {
  var Animation;

  Animation = (function() {
    function Animation() {}

    Animation.prototype.slideDown = function(elem, props) {
      var cstyle, h, margin_bottom, margin_top, padding_bottom, padding_top, transition;
      if (elem.offsetTop > 2000) {
        return;
      }
      h = elem.offsetHeight;
      cstyle = window.getComputedStyle(elem);
      margin_top = cstyle.marginTop;
      margin_bottom = cstyle.marginBottom;
      padding_top = cstyle.paddingTop;
      padding_bottom = cstyle.paddingBottom;
      transition = cstyle.transition;
      elem.style.boxSizing = "border-box";
      elem.style.overflow = "hidden";
      elem.style.transform = "scale(0.6)";
      elem.style.opacity = "0";
      elem.style.height = "0px";
      elem.style.marginTop = "0px";
      elem.style.marginBottom = "0px";
      elem.style.paddingTop = "0px";
      elem.style.paddingBottom = "0px";
      elem.style.transition = "none";
      setTimeout((function() {
        elem.className += " animate-inout";
        elem.style.height = h + "px";
        elem.style.transform = "scale(1)";
        elem.style.opacity = "1";
        elem.style.marginTop = margin_top;
        elem.style.marginBottom = margin_bottom;
        elem.style.paddingTop = padding_top;
        return elem.style.paddingBottom = padding_bottom;
      }), 1);
      return elem.addEventListener("transitionend", function() {
        elem.classList.remove("animate-inout");
        elem.style.transition = elem.style.transform = elem.style.opacity = elem.style.height = null;
        elem.style.boxSizing = elem.style.marginTop = elem.style.marginBottom = null;
        elem.style.paddingTop = elem.style.paddingBottom = elem.style.overflow = null;
        return elem.removeEventListener("transitionend", arguments.callee, false);
      });
    };

    Animation.prototype.slideUp = function(elem, remove_func, props) {
      if (elem.offsetTop > 1000) {
        return remove_func();
      }
      elem.className += " animate-back";
      elem.style.boxSizing = "border-box";
      elem.style.height = elem.offsetHeight + "px";
      elem.style.overflow = "hidden";
      elem.style.transform = "scale(1)";
      elem.style.opacity = "1";
      elem.style.pointerEvents = "none";
      setTimeout((function() {
        elem.style.height = "0px";
        elem.style.marginTop = "0px";
        elem.style.marginBottom = "0px";
        elem.style.paddingTop = "0px";
        elem.style.paddingBottom = "0px";
        elem.style.transform = "scale(0.8)";
        elem.style.borderTopWidth = "0px";
        elem.style.borderBottomWidth = "0px";
        return elem.style.opacity = "0";
      }), 1);
      return elem.addEventListener("transitionend", function(e) {
        if (e.propertyName === "opacity" || e.elapsedTime >= 0.6) {
          elem.removeEventListener("transitionend", arguments.callee, false);
          return remove_func();
        }
      });
    };

    Animation.prototype.slideUpInout = function(elem, remove_func, props) {
      elem.className += " animate-inout";
      elem.style.boxSizing = "border-box";
      elem.style.height = elem.offsetHeight + "px";
      elem.style.overflow = "hidden";
      elem.style.transform = "scale(1)";
      elem.style.opacity = "1";
      elem.style.pointerEvents = "none";
      setTimeout((function() {
        elem.style.height = "0px";
        elem.style.marginTop = "0px";
        elem.style.marginBottom = "0px";
        elem.style.paddingTop = "0px";
        elem.style.paddingBottom = "0px";
        elem.style.transform = "scale(0.8)";
        elem.style.borderTopWidth = "0px";
        elem.style.borderBottomWidth = "0px";
        return elem.style.opacity = "0";
      }), 1);
      return elem.addEventListener("transitionend", function(e) {
        if (e.propertyName === "opacity" || e.elapsedTime >= 0.6) {
          elem.removeEventListener("transitionend", arguments.callee, false);
          return remove_func();
        }
      });
    };

    Animation.prototype.showRight = function(elem, props) {
      elem.className += " animate";
      elem.style.opacity = 0;
      elem.style.transform = "TranslateX(-20px) Scale(1.01)";
      setTimeout((function() {
        elem.style.opacity = 1;
        return elem.style.transform = "TranslateX(0px) Scale(1)";
      }), 1);
      return elem.addEventListener("transitionend", function() {
        elem.classList.remove("animate");
        return elem.style.transform = elem.style.opacity = null;
      });
    };

    Animation.prototype.show = function(elem, props) {
      var delay, ref;
      delay = ((ref = arguments[arguments.length - 2]) != null ? ref.delay : void 0) * 1000 || 1;
      elem.style.opacity = 0;
      setTimeout((function() {
        return elem.className += " animate";
      }), 1);
      setTimeout((function() {
        return elem.style.opacity = 1;
      }), delay);
      return elem.addEventListener("transitionend", function() {
        elem.classList.remove("animate");
        elem.style.opacity = null;
        return elem.removeEventListener("transitionend", arguments.callee, false);
      });
    };

    Animation.prototype.hide = function(elem, remove_func, props) {
      var delay, ref;
      delay = ((ref = arguments[arguments.length - 2]) != null ? ref.delay : void 0) * 1000 || 1;
      elem.className += " animate";
      setTimeout((function() {
        return elem.style.opacity = 0;
      }), delay);
      return elem.addEventListener("transitionend", function(e) {
        if (e.propertyName === "opacity") {
          return remove_func();
        }
      });
    };

    Animation.prototype.addVisibleClass = function(elem, props) {
      return setTimeout(function() {
        return elem.classList.add("visible");
      });
    };

    return Animation;

  })();

  window.Animation = new Animation();

}).call(this);

/* ---- utils/Dollar.coffee ---- */


(function() {
  window.$ = function(selector) {
    if (selector.startsWith("#")) {
      return document.getElementById(selector.replace("#", ""));
    }
  };

}).call(this);

/* ---- utils/ZeroFrame.coffee ---- */


(function() {
  var ZeroFrame,
    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    hasProp = {}.hasOwnProperty;

  ZeroFrame = (function(superClass) {
    extend(ZeroFrame, superClass);

    function ZeroFrame(url) {
      this.onCloseWebsocket = bind(this.onCloseWebsocket, this);
      this.onOpenWebsocket = bind(this.onOpenWebsocket, this);
      this.onRequest = bind(this.onRequest, this);
      this.onMessage = bind(this.onMessage, this);
      this.url = url;
      this.waiting_cb = {};
      this.wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1");
      this.connect();
      this.next_message_id = 1;
      this.history_state = {};
      this.init();
    }

    ZeroFrame.prototype.init = function() {
      return this;
    };

    ZeroFrame.prototype.connect = function() {
      this.target = window.parent;
      window.addEventListener("message", this.onMessage, false);
      this.cmd("innerReady");
      window.addEventListener("beforeunload", (function(_this) {
        return function(e) {
          _this.log("save scrollTop", window.pageYOffset);
          _this.history_state["scrollTop"] = window.pageYOffset;
          return _this.cmd("wrapperReplaceState", [_this.history_state, null]);
        };
      })(this));
      return this.cmd("wrapperGetState", [], (function(_this) {
        return function(state) {
          if (state != null) {
            _this.history_state = state;
          }
          _this.log("restore scrollTop", state, window.pageYOffset);
          if (window.pageYOffset === 0 && state) {
            return window.scroll(window.pageXOffset, state.scrollTop);
          }
        };
      })(this));
    };

    ZeroFrame.prototype.onMessage = function(e) {
      var cmd, message;
      message = e.data;
      cmd = message.cmd;
      if (cmd === "response") {
        if (this.waiting_cb[message.to] != null) {
          return this.waiting_cb[message.to](message.result);
        } else {
          return this.log("Websocket callback not found:", message);
        }
      } else if (cmd === "wrapperReady") {
        return this.cmd("innerReady");
      } else if (cmd === "ping") {
        return this.response(message.id, "pong");
      } else if (cmd === "wrapperOpenedWebsocket") {
        return this.onOpenWebsocket();
      } else if (cmd === "wrapperClosedWebsocket") {
        return this.onCloseWebsocket();
      } else {
        return this.onRequest(cmd, message.params);
      }
    };

    ZeroFrame.prototype.onRequest = function(cmd, message) {
      return this.log("Unknown request", message);
    };

    ZeroFrame.prototype.response = function(to, result) {
      return this.send({
        "cmd": "response",
        "to": to,
        "result": result
      });
    };

    ZeroFrame.prototype.cmd = function(cmd, params, cb) {
      if (params == null) {
        params = {};
      }
      if (cb == null) {
        cb = null;
      }
      return this.send({
        "cmd": cmd,
        "params": params
      }, cb);
    };

    ZeroFrame.prototype.send = function(message, cb) {
      if (cb == null) {
        cb = null;
      }
      message.wrapper_nonce = this.wrapper_nonce;
      message.id = this.next_message_id;
      this.next_message_id += 1;
      this.target.postMessage(message, "*");
      if (cb) {
        return this.waiting_cb[message.id] = cb;
      }
    };

    ZeroFrame.prototype.onOpenWebsocket = function() {
      return this.log("Websocket open");
    };

    ZeroFrame.prototype.onCloseWebsocket = function() {
      return this.log("Websocket close");
    };

    return ZeroFrame;

  })(Class);

  window.ZeroFrame = ZeroFrame;

}).call(this);

/* ---- ConfigStorage.coffee ---- */


(function() {
  var ConfigStorage,
    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    hasProp = {}.hasOwnProperty;

  ConfigStorage = (function(superClass) {
    extend(ConfigStorage, superClass);

    function ConfigStorage(config) {
      this.config = config;
      this.createSection = bind(this.createSection, this);
      this.items = [];
      this.createSections();
      this.setValues(this.config);
    }

    ConfigStorage.prototype.setValues = function(values) {
      var i, item, len, ref, results, section;
      ref = this.items;
      results = [];
      for (i = 0, len = ref.length; i < len; i++) {
        section = ref[i];
        results.push((function() {
          var j, len1, ref1, results1;
          ref1 = section.items;
          results1 = [];
          for (j = 0, len1 = ref1.length; j < len1; j++) {
            item = ref1[j];
            if (!values[item.key]) {
              continue;
            }
            item.value = this.formatValue(values[item.key].value);
            item["default"] = this.formatValue(values[item.key]["default"]);
            item.pending = values[item.key].pending;
            results1.push(values[item.key].item = item);
          }
          return results1;
        }).call(this));
      }
      return results;
    };

    ConfigStorage.prototype.formatValue = function(value) {
      if (!value) {
        return false;
      } else if (typeof value === "object") {
        return value.join("\n");
      } else if (typeof value === "number") {
        return value.toString();
      } else {
        return value;
      }
    };

    ConfigStorage.prototype.deformatValue = function(value, type) {
      if (type === "object" && typeof value === "string") {
        if (!value.length) {
          return value = null;
        } else {
          return value.split("\n");
        }
      }
      if (type === "boolean" && !value) {
        return false;
      } else if (type === "number") {
        if (typeof value === "number") {
          return value.toString();
        } else if (!value) {
          return "0";
        } else {
          return value;
        }
      } else {
        return value;
      }
    };

    ConfigStorage.prototype.createSections = function() {
      var section;
      section = this.createSection("Web Interface");
      section.items.push({
        key: "open_browser",
        title: "Open web browser on ZeroNet startup",
        type: "checkbox"
      });
      section = this.createSection("Network");
      section.items.push({
        key: "offline",
        title: "Offline mode",
        type: "checkbox",
        description: "Disable network communication."
      });
      section.items.push({
        key: "fileserver_ip_type",
        title: "File server network",
        type: "select",
        options: [
          {
            title: "IPv4",
            value: "ipv4"
          }, {
            title: "IPv6",
            value: "ipv6"
          }, {
            title: "Dual (IPv4 & IPv6)",
            value: "dual"
          }
        ],
        description: "Accept incoming peers using IPv4 or IPv6 address. (default: dual)"
      });
      section.items.push({
        key: "fileserver_port",
        title: "File server port",
        type: "text",
        valid_pattern: /[0-9]*/,
        description: "Other peers will use this port to reach your served sites. (default: randomize)"
      });
      section.items.push({
        key: "ip_external",
        title: "File server external ip",
        type: "textarea",
        placeholder: "Detect automatically",
        description: "Your file server is accessible on these ips. (default: detect automatically)"
      });
      section.items.push({
        title: "Tor",
        key: "tor",
        type: "select",
        options: [
          {
            title: "Disable",
            value: "disable"
          }, {
            title: "Enable",
            value: "enable"
          }, {
            title: "Always",
            value: "always"
          }
        ],
        description: ["Disable: Don't connect to peers on Tor network", h("br"), "Enable: Only use Tor for Tor network peers", h("br"), "Always: Use Tor for every connections to hide your IP address (slower)"]
      });
      section.items.push({
        title: "Use Tor bridges",
        key: "tor_use_bridges",
        type: "checkbox",
        description: "Use obfuscated bridge relays to avoid network level Tor block (even slower)",
        isHidden: function() {
          return !Page.server_info.tor_has_meek_bridges;
        }
      });
      section.items.push({
        title: "Trackers",
        key: "trackers",
        type: "textarea",
        description: "Discover new peers using these adresses"
      });
      section.items.push({
        title: "Trackers files",
        key: "trackers_file",
        type: "textarea",
        description: "Load additional list of torrent trackers dynamically, from a file",
        placeholder: "Eg.: {data_dir}/trackers.json",
        value_pos: "fullwidth"
      });
      section.items.push({
        title: "Proxy for tracker connections",
        key: "trackers_proxy",
        type: "select",
        options: [
          {
            title: "Custom",
            value: ""
          }, {
            title: "Tor",
            value: "tor"
          }, {
            title: "Disable",
            value: "disable"
          }
        ],
        isHidden: function() {
          return Page.values["tor"] === "always";
        }
      });
      section.items.push({
        title: "Custom socks proxy address for trackers",
        key: "trackers_proxy",
        type: "text",
        placeholder: "Eg.: 127.0.0.1:1080",
        value_pos: "fullwidth",
        valid_pattern: /.+:[0-9]+/,
        isHidden: (function(_this) {
          return function() {
            var ref;
            return (ref = Page.values["trackers_proxy"]) === "tor" || ref === "disable";
          };
        })(this)
      });
      section = this.createSection("Performance");
      section.items.push({
        key: "log_level",
        title: "Level of logging to file",
        type: "select",
        options: [
          {
            title: "Everything",
            value: "DEBUG"
          }, {
            title: "Only important messages",
            value: "INFO"
          }, {
            title: "Only errors",
            value: "ERROR"
          }
        ]
      });
      section.items.push({
        key: "threads_fs_read",
        title: "Threads for async file system reads",
        type: "select",
        options: [
          {
            title: "Sync read",
            value: 0
          }, {
            title: "1 thread",
            value: 1
          }, {
            title: "2 threads",
            value: 2
          }, {
            title: "3 threads",
            value: 3
          }, {
            title: "4 threads",
            value: 4
          }, {
            title: "5 threads",
            value: 5
          }, {
            title: "10 threads",
            value: 10
          }
        ]
      });
      section.items.push({
        key: "threads_fs_write",
        title: "Threads for async file system writes",
        type: "select",
        options: [
          {
            title: "Sync write",
            value: 0
          }, {
            title: "1 thread",
            value: 1
          }, {
            title: "2 threads",
            value: 2
          }, {
            title: "3 threads",
            value: 3
          }, {
            title: "4 threads",
            value: 4
          }, {
            title: "5 threads",
            value: 5
          }, {
            title: "10 threads",
            value: 10
          }
        ]
      });
      section.items.push({
        key: "threads_crypt",
        title: "Threads for cryptographic functions",
        type: "select",
        options: [
          {
            title: "Sync execution",
            value: 0
          }, {
            title: "1 thread",
            value: 1
          }, {
            title: "2 threads",
            value: 2
          }, {
            title: "3 threads",
            value: 3
          }, {
            title: "4 threads",
            value: 4
          }, {
            title: "5 threads",
            value: 5
          }, {
            title: "10 threads",
            value: 10
          }
        ]
      });
      return section.items.push({
        key: "threads_db",
        title: "Threads for database operations",
        type: "select",
        options: [
          {
            title: "Sync execution",
            value: 0
          }, {
            title: "1 thread",
            value: 1
          }, {
            title: "2 threads",
            value: 2
          }, {
            title: "3 threads",
            value: 3
          }, {
            title: "4 threads",
            value: 4
          }, {
            title: "5 threads",
            value: 5
          }, {
            title: "10 threads",
            value: 10
          }
        ]
      });
    };

    ConfigStorage.prototype.createSection = function(title) {
      var section;
      section = {};
      section.title = title;
      section.items = [];
      this.items.push(section);
      return section;
    };

    return ConfigStorage;

  })(Class);

  window.ConfigStorage = ConfigStorage;

}).call(this);


/* ---- ConfigView.coffee ---- */


(function() {
  var ConfigView,
    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    hasProp = {}.hasOwnProperty;

  ConfigView = (function(superClass) {
    extend(ConfigView, superClass);

    function ConfigView() {
      this.renderValueSelect = bind(this.renderValueSelect, this);
      this.renderValueCheckbox = bind(this.renderValueCheckbox, this);
      this.renderValueTextarea = bind(this.renderValueTextarea, this);
      this.autosizeTextarea = bind(this.autosizeTextarea, this);
      this.renderValueText = bind(this.renderValueText, this);
      this.handleCheckboxChange = bind(this.handleCheckboxChange, this);
      this.handleInputChange = bind(this.handleInputChange, this);
      this.renderSectionItem = bind(this.renderSectionItem, this);
      this.handleResetClick = bind(this.handleResetClick, this);
      this.renderSection = bind(this.renderSection, this);
      this;
    }

    ConfigView.prototype.render = function() {
      return this.config_storage.items.map(this.renderSection);
    };

    ConfigView.prototype.renderSection = function(section) {
      return h("div.section", {
        key: section.title
      }, [h("h2", section.title), h("div.config-items", section.items.map(this.renderSectionItem))]);
    };

    ConfigView.prototype.handleResetClick = function(e) {
      var config_key, default_value, node, ref;
      node = e.currentTarget;
      config_key = node.attributes.config_key.value;
      default_value = (ref = node.attributes.default_value) != null ? ref.value : void 0;
      return Page.cmd("wrapperConfirm", ["Reset " + config_key + " value?", "Reset to default"], (function(_this) {
        return function(res) {
          if (res) {
            _this.values[config_key] = default_value;
          }
          return Page.projector.scheduleRender();
        };
      })(this));
    };

    ConfigView.prototype.renderSectionItem = function(item) {
      var marker_title, ref, value_changed, value_default, value_pos;
      value_pos = item.value_pos;
      if (item.type === "textarea") {
        if (value_pos == null) {
          value_pos = "fullwidth";
        }
      } else {
        if (value_pos == null) {
          value_pos = "right";
        }
      }
      value_changed = this.config_storage.formatValue(this.values[item.key]) !== item.value;
      value_default = this.config_storage.formatValue(this.values[item.key]) === item["default"];
      if ((ref = item.key) === "open_browser" || ref === "fileserver_port") {
        value_default = true;
      }
      marker_title = "Changed from default value: " + item["default"] + " -> " + this.values[item.key];
      if (item.pending) {
        marker_title += " (change pending until client restart)";
      }
      if (typeof item.isHidden === "function" ? item.isHidden() : void 0) {
        return null;
      }
      return h("div.config-item", {
        key: item.title,
        enterAnimation: Animation.slideDown,
        exitAnimation: Animation.slideUpInout
      }, [
        h("div.title", [h("h3", item.title), h("div.description", item.description)]), h("div.value.value-" + value_pos, item.type === "select" ? this.renderValueSelect(item) : item.type === "checkbox" ? this.renderValueCheckbox(item) : item.type === "textarea" ? this.renderValueTextarea(item) : this.renderValueText(item), h("a.marker", {
          href: "#Reset",
          title: marker_title,
          onclick: this.handleResetClick,
          config_key: item.key,
          default_value: item["default"],
          classes: {
            "default": value_default,
            changed: value_changed,
            visible: !value_default || value_changed || item.pending,
            pending: item.pending
          }
        }, "\u2022"))
      ]);
    };

    ConfigView.prototype.handleInputChange = function(e) {
      var config_key, node;
      node = e.target;
      config_key = node.attributes.config_key.value;
      this.values[config_key] = node.value;
      return Page.projector.scheduleRender();
    };

    ConfigView.prototype.handleCheckboxChange = function(e) {
      var config_key, node, value;
      node = e.currentTarget;
      config_key = node.attributes.config_key.value;
      value = !node.classList.contains("checked");
      this.values[config_key] = value;
      return Page.projector.scheduleRender();
    };

    ConfigView.prototype.renderValueText = function(item) {
      var value;
      value = this.values[item.key];
      if (!value) {
        value = "";
      }
      return h("input.input-" + item.type, {
        type: item.type,
        config_key: item.key,
        value: value,
        placeholder: item.placeholder,
        oninput: this.handleInputChange
      });
    };

    ConfigView.prototype.autosizeTextarea = function(e) {
      var h, height_before, node, scrollh;
      if (e.currentTarget) {
        node = e.currentTarget;
      } else {
        node = e;
      }
      height_before = node.style.height;
      if (height_before) {
        node.style.height = "0px";
      }
      h = node.offsetHeight;
      scrollh = node.scrollHeight + 20;
      if (scrollh > h) {
        return node.style.height = scrollh + "px";
      } else {
        return node.style.height = height_before;
      }
    };

    ConfigView.prototype.renderValueTextarea = function(item) {
      var value;
      value = this.values[item.key];
      if (!value) {
        value = "";
      }
      return h("textarea.input-" + item.type + ".input-text", {
        type: item.type,
        config_key: item.key,
        oninput: this.handleInputChange,
        afterCreate: this.autosizeTextarea,
        updateAnimation: this.autosizeTextarea,
        value: value,
        placeholder: item.placeholder
      });
    };

    ConfigView.prototype.renderValueCheckbox = function(item) {
      var checked;
      if (this.values[item.key] && this.values[item.key] !== "False") {
        checked = true;
      } else {
        checked = false;
      }
      return h("div.checkbox", {
        onclick: this.handleCheckboxChange,
        config_key: item.key,
        classes: {
          checked: checked
        }
      }, h("div.checkbox-skin"));
    };

    ConfigView.prototype.renderValueSelect = function(item) {
      return h("select.input-select", {
        config_key: item.key,
        oninput: this.handleInputChange
      }, item.options.map((function(_this) {
        return function(option) {
          return h("option", {
            selected: option.value.toString() === _this.values[item.key],
            value: option.value
          }, option.title);
        };
      })(this)));
    };

    return ConfigView;

  })(Class);

  window.ConfigView = ConfigView;

}).call(this);

/* ---- UiConfig.coffee ---- */


(function() {
  var UiConfig,
    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    hasProp = {}.hasOwnProperty;

  window.h = maquette.h;

  UiConfig = (function(superClass) {
    extend(UiConfig, superClass);

    function UiConfig() {
      this.renderBottomRestart = bind(this.renderBottomRestart, this);
      this.handleRestartClick = bind(this.handleRestartClick, this);
      this.renderBottomSave = bind(this.renderBottomSave, this);
      this.handleSaveClick = bind(this.handleSaveClick, this);
      this.render = bind(this.render, this);
      this.saveValue = bind(this.saveValue, this);
      this.saveValues = bind(this.saveValues, this);
      this.getValuesPending = bind(this.getValuesPending, this);
      this.getValuesChanged = bind(this.getValuesChanged, this);
      this.createProjector = bind(this.createProjector, this);
      this.updateConfig = bind(this.updateConfig, this);
      this.onOpenWebsocket = bind(this.onOpenWebsocket, this);
      return UiConfig.__super__.constructor.apply(this, arguments);
    }

    UiConfig.prototype.init = function() {
      this.save_visible = true;
      this.config = null;
      this.values = null;
      this.config_view = new ConfigView();
      return window.onbeforeunload = (function(_this) {
        return function() {
          if (_this.getValuesChanged().length > 0) {
            return true;
          } else {
            return null;
          }
        };
      })(this);
    };

    UiConfig.prototype.onOpenWebsocket = function() {
      this.cmd("wrapperSetTitle", "Config - ZeroNet");
      this.cmd("serverInfo", {}, (function(_this) {
        return function(server_info) {
          return _this.server_info = server_info;
        };
      })(this));
      this.restart_loading = false;
      return this.updateConfig();
    };

    UiConfig.prototype.updateConfig = function(cb) {
      return this.cmd("configList", [], (function(_this) {
        return function(res) {
          var item, key, value;
          _this.config = res;
          _this.values = {};
          _this.config_storage = new ConfigStorage(_this.config);
          _this.config_view.values = _this.values;
          _this.config_view.config_storage = _this.config_storage;
          for (key in res) {
            item = res[key];
            value = item.value;
            _this.values[key] = _this.config_storage.formatValue(value);
          }
          _this.projector.scheduleRender();
          return typeof cb === "function" ? cb() : void 0;
        };
      })(this));
    };

    UiConfig.prototype.createProjector = function() {
      this.projector = maquette.createProjector();
      this.projector.replace($("#content"), this.render);
      this.projector.replace($("#bottom-save"), this.renderBottomSave);
      return this.projector.replace($("#bottom-restart"), this.renderBottomRestart);
    };

    UiConfig.prototype.getValuesChanged = function() {
      var key, ref, ref1, value, values_changed;
      values_changed = [];
      ref = this.values;
      for (key in ref) {
        value = ref[key];
        if (this.config_storage.formatValue(value) !== this.config_storage.formatValue((ref1 = this.config[key]) != null ? ref1.value : void 0)) {
          values_changed.push({
            key: key,
            value: value
          });
        }
      }
      return values_changed;
    };

    UiConfig.prototype.getValuesPending = function() {
      var item, key, ref, values_pending;
      values_pending = [];
      ref = this.config;
      for (key in ref) {
        item = ref[key];
        if (item.pending) {
          values_pending.push(key);
        }
      }
      return values_pending;
    };

    UiConfig.prototype.saveValues = function(cb) {
      var base, changed_values, default_value, i, item, j, last, len, match, message, results, value, value_same_as_default;
      changed_values = this.getValuesChanged();
      results = [];
      for (i = j = 0, len = changed_values.length; j < len; i = ++j) {
        item = changed_values[i];
        last = i === changed_values.length - 1;
        value = this.config_storage.deformatValue(item.value, typeof this.config[item.key]["default"]);
        default_value = this.config_storage.deformatValue(this.config[item.key]["default"], typeof this.config[item.key]["default"]);
        value_same_as_default = JSON.stringify(default_value) === JSON.stringify(value);
        if (this.config[item.key].item.valid_pattern && !(typeof (base = this.config[item.key].item).isHidden === "function" ? base.isHidden() : void 0)) {
          match = value.match(this.config[item.key].item.valid_pattern);
          if (!match || match[0] !== value) {
            message = "Invalid value of " + this.config[item.key].item.title + ": " + value + " (does not matches " + this.config[item.key].item.valid_pattern + ")";
            Page.cmd("wrapperNotification", ["error", message]);
            cb(false);
            break;
          }
        }
        if (value_same_as_default) {
          value = null;
        }
        results.push(this.saveValue(item.key, value, last ? cb : null));
      }
      return results;
    };

    UiConfig.prototype.saveValue = function(key, value, cb) {
      if (key === "open_browser") {
        if (value) {
          value = "default_browser";
        } else {
          value = "False";
        }
      }
      return Page.cmd("configSet", [key, value], (function(_this) {
        return function(res) {
          if (res !== "ok") {
            Page.cmd("wrapperNotification", ["error", res.error]);
          }
          return typeof cb === "function" ? cb(true) : void 0;
        };
      })(this));
    };

    UiConfig.prototype.render = function() {
      if (!this.config) {
        return h("div.content");
      }
      return h("div.content", [this.config_view.render()]);
    };

    UiConfig.prototype.handleSaveClick = function() {
      this.save_loading = true;
      this.logStart("Save");
      this.saveValues((function(_this) {
        return function(success) {
          _this.save_loading = false;
          _this.logEnd("Save");
          if (success) {
            _this.updateConfig();
          }
          return Page.projector.scheduleRender();
        };
      })(this));
      return false;
    };

    UiConfig.prototype.renderBottomSave = function() {
      var values_changed;
      values_changed = this.getValuesChanged();
      return h("div.bottom.bottom-save", {
        classes: {
          visible: values_changed.length
        }
      }, h("div.bottom-content", [
        h("div.title", values_changed.length + " configuration item value changed"), h("a.button.button-submit.button-save", {
          href: "#Save",
          classes: {
            loading: this.save_loading
          },
          onclick: this.handleSaveClick
        }, "Save settings")
      ]));
    };

    UiConfig.prototype.handleRestartClick = function() {
      this.restart_loading = true;
      Page.cmd("serverShutdown", {
        restart: true
      });
      Page.projector.scheduleRender();
      return false;
    };

    UiConfig.prototype.renderBottomRestart = function() {
      var values_changed, values_pending;
      values_pending = this.getValuesPending();
      values_changed = this.getValuesChanged();
      return h("div.bottom.bottom-restart", {
        classes: {
          visible: values_pending.length && !values_changed.length
        }
      }, h("div.bottom-content", [
        h("div.title", "Some changed settings requires restart"), h("a.button.button-submit.button-restart", {
          href: "#Restart",
          classes: {
            loading: this.restart_loading
          },
          onclick: this.handleRestartClick
        }, "Restart ZeroNet client")
      ]));
    };

    return UiConfig;

  })(ZeroFrame);

  window.Page = new UiConfig();

  window.Page.createProjector();

}).call(this);