﻿
interface ViewOptions
{
    el?: any;
}

abstract class View implements IDestroyable
{
    // Attributes

    // HTML tags are stored as jQuery object
    private _parentEl: JQuery = null;       // Parent tag to which view is attached and rendered into.
    private _el: JQuery = null;             // Tag containing contents of view that is attached to parent.

    // Constructor
    constructor(parentEl: any, options?: ViewOptions)
    {
        this.ParentEl = parentEl;
        if (options && options.el)
        {
            // Tag to contain view contents has been provided
            this.El = options.el;
        }
        else
        {
            // By default view contents are rendered into a <div> tag
            this.El = $(document.createElement('div'));
        }
        this.Attach();
    }

    // Properties
    private set ParentEl(el: any)
    {
        this._parentEl = this.GetJQueryObject(el);
    }

    private set El(el: any)
    {
        this._el = this.GetJQueryObject(el);
    }

    get Parent(): JQuery
    {
        return this._parentEl;
    }

    protected get View(): JQuery
    {
        // The container tag into which the view contents should be placed
        return this._el;
    }

    // Methods
    protected GetJQueryObject(el: any): JQuery
    {
        // If input is a JS HTML object or a text selection e.g. "#someId"
        // then we let jQuery convert it to a jQuery object using $(tag).
        // If was a jQuery object already we just use it.
        return el instanceof $ ? el : $(el);
    }

    //SetHtmlTag(el: any): void
    //{
    //    // Change the view container element e.g. if you want to
    //    // dynamically change the contents.
    //    this.Detach();
    //    this.El = el;       // Add the new view container tag.
    //}

    protected $(selector: any): JQuery
    {
        return this._el.find(selector);
    }

    Attach(): void
    {
        if (this._parentEl.find(this._el).length == 0) // Make sure it isn't already attached
        {
            this._el.appendTo(this._parentEl);  // Add the view to the parent element  
        }
    }

    Detach(): void
    {
        this._el.detach(); // Remove the view from the parent but you can reattach it
    }

    Move(newParentEl: any): JQuery
    {
        var oldParent = this._parentEl;
        this.Detach();
        this.ParentEl = newParentEl;
        this.Attach();
        return oldParent;
    }

    Render(): void
    {
        this.UnbindEvents();
        this.RenderView(this._el);
        this.BindEvents();
    }

    protected abstract BindEvents(): void;
    protected abstract UnbindEvents(): void;

    // Load HTML from the server URL into the container tag.
    //LoadHTML(url: string): void
    //{
    //    this._el.load(url);
    //}

    Destroy(): void
    {
        this.UnbindEvents();

        // Detach from parent and destroy all jQuery objects
        // associated with view. If detached will just destroy
        // contents of view.
        this._el.remove();
        this._el = null;
        this._parentEl = null;
    }

    protected abstract RenderView(el: JQuery): void;
}  