
/**
 * Basic DMK Scripts
 *
 * @copyright Copyright (c) 2014 DMK E-BUSINESS GmbH <dev@dmk-ebusiness.de>
 * @license http://www.gnu.org/licenses/lgpl.html
 *          GNU Lesser General Public License, version 3 or later
 * @version 0.1.0
 * @requires jQuery
 * @author Michael Wagner <michael.wagner@dmk-ebusiness.de>
 */

(function(w, $){
    "use strict";
    var _undefined, Base, DMK, VERSION = "0.1.0";

    // the base MVC Object
    Base = function Base (record) {
        this.data = typeof record === "object" ? record : {};
    };
    // add MVC Methods (getter, setter, ...)
    Base.prototype.hasData = function (key) {
        return typeof this.data[key] !== "undefined";
    };
    Base.prototype.setData = function (key, value) {
        if (typeof value === "undefined" && typeof key === "object") {
            this.data = key;
        } else {
            this.data[key] = value;
        }
        return this.getData(key);
    };
    Base.prototype.getData = function (key) {
        return typeof key === "undefined" ? this.data : this.data[key];
    };

    // add basic extend funcitons
    Base.extend = function(ParentInstance, Class, params) {
        Class.prototype = ParentInstance;
        Class.prototype.parent = function() { return ParentInstance; };
        Class.prototype.constructor = Class;
        Class.extend = function(Child, childParams) {
            return Base.extend(new Class(childParams), Child, childParams);
        };
        return Class;
    };
    Base.prototype.extend = function (Class, params) {
        return Base.extend(this.getInstance(this, true, params), Class, params);
    };
    Base.prototype.getInstance = function (Instance, forceNewObject, params) {
        Instance = this.isDefined(Instance) ? Instance : this;
        forceNewObject = this.isDefined(forceNewObject) ? forceNewObject : false;
        params = this.isDefined(params) ? params : {};
        if (this.isFunction(Instance)) {
            return new Instance(params);
        }
        if (this.isObject(Instance)) {
            return forceNewObject && this.isFunction(Instance.constructor) ? new Instance.constructor(params) : Instance;
        }
        return ;
    };
    Base.prototype.getClassName = function () {
        var matches = this.constructor.toString().match(/function (.{1,})\(/);
        return (matches && matches.length > 1) ? matches[1] : _undefined;
    };

    // add some basic functions
    Base.prototype.isDefined = function (val) {
        return typeof val !== "undefined";
    };
    Base.prototype.isObject = function (val) {
        return typeof val === "object";
    };
    Base.prototype.isObjectJQuery = function (val) {
        return val instanceof jQuery;
    };
    Base.prototype.isFunction = function (val) {
        return typeof val === "function";
    };
    Base.prototype.isNumeric = function (val) {
        return !isNaN(parseFloat(val, 10)) && isFinite(val);
    };
    Base.prototype.isString = function (val) {
        return typeof val === "string";
    };

    // The global basic object
    DMK = function DMK () {
        var _DMK = this, _Libraries = [];
        this.Version = VERSION;
        this.Base = new Base({"name" : "Base", "description" : "Required base functionalities of DMK", "version" : VERSION});
        this.Objects = {
            add : function (Object, Name) {
                if (!_DMK.Base.isDefined(Name)) {
                    var Instance = new Object();
                    Name = Instance.getClassName();
                }
                this[Name] = Object;
            },
            extend : function (Name, Class, params) {
                if (!this[Name]) {
                    return null;
                }
                this[Name] = this[Name].extend(Class, params);
                return this[Name];
            },
            getInstance : function(Name, params) {
                return this[Name] ? new this[Name](params) : null;
            }
        };
        this.Libraries = {
            add : function (Object) {
                var Instance = _DMK.Base.getInstance(Object),
                    Name = Instance.getClassName(),
                    Class = Instance.constructor
                ;
                _DMK.Objects.add(Class, Name);
                _Libraries.push(Name);
                _DMK[Name] = Instance; // lib mappen
                return Instance;
            },
            init : function (name) {
                var object = _DMK[name],
                    init = function(){};
                if (!_DMK.Base.isDefined(object) || object.initialized === true) {
                    return ; // continue
                }
                init = object.initialize || object.init || init;
                if (_DMK.Base.isFunction(init)) {
                    init.call(object); // initialisieren
                }
                object.initialized = true; // status merken
                return object;
            }
        };
        this.init = function() {
            // automatisch alle Libraries initialisieren.
            $.each(
                _Libraries,
                function (index, name) {
                    _DMK.Libraries.init(name);
                }
            );
        };
        // auto init on document ready
        $(this.init);
    };// end DMK

    // singelton erstellen und global unter DMK bereitstellen
    w.DMK = new DMK();
})(window, jQuery);

// A smal storage addon
(function(DMK){
    var Registry = function Registry() {
        this.setData("version", "0.1.0");
        this.buildCacheId = function(params) {
            return JSON.stringify(params);
        };
    };
    DMK.Libraries.add(DMK.Base.extend(Registry));
})(DMK);

/**
 * Request
 *
 * Library for Ajax Calls
 *
 * @copyright Copyright (c) 2014 DMK E-BUSINESS GmbH <dev@dmk-ebusiness.de>
 * @license http://www.gnu.org/licenses/lgpl.html
 *             GNU Lesser General Public License, version 3 or later
 * @version 0.1.1
 * @requires Base, Registry
 * @author Michael Wagner <michael.wagner@dmk-ebusiness.de>
 */

(function(DMK, w, $){
    "use strict";

    var Request, VERSION = "0.1.1";

    Request = DMK.Base.extend(
        function Request() {
            this.setData("version", VERSION);
        }
    );
    // Wir Führen einen Ajax Call aus!
    Request.prototype.doCall = function(urlOrElement, parameters) {
        var
            _request = this,
            cache = this.getCache(),
            cacheable = this.isObject(cache),
            cacheId = ""
        ;

        // Parameter und URL sammeln.
        if (!_request.isObject(parameters)) {
            parameters = {};
        }
        _request.prepareParameters(urlOrElement, parameters),

        // Event Triggern
        _request.onStart({}, parameters);

        cacheId = cacheable ? cache.buildCacheId(parameters) : cacheId;
        // den Cache nach einem bereits getaetigten Request fragen.
        if (cacheable && cache.hasData(cacheId)) {
            _request.onSuccess(cache.getData(cacheId), parameters);
            _request.handleHistoryOnSuccess(parameters);
            _request.onComplete({}, parameters);
        }
        // Den Ajax Request absenden.
        else {
            var ajaxOptions =
            {
                url : parameters.href,
                type : parameters.requestType,
                dataType : "html", // make configurable
                success : function(data, textStatus, jqXHR) {
                    // Cachen!
                    if (cacheable) {
                        cache.setData(cacheId, data);
                    }
                    _request.handleHistoryOnSuccess(parameters);
                    return _request.onSuccess(data, parameters, textStatus, jqXHR);
                },
                error : function(jqXHR, textStatus, errorThrown) {
                    return _request.onFailure(arguments, parameters, jqXHR, textStatus, errorThrown);
                },
                complete : function(jqXHR, textStatus) {
                    return _request.onComplete(arguments, parameters, jqXHR, textStatus);
                }
            };

            if (
                !_request.isObjectJQuery(urlOrElement) ||
                !urlOrElement.hasClass('ajax-dont-add-parameters-to-request')
            ) {
                ajaxOptions.data = parameters;
            }

            // haben wir ein Formular?
            if (
                _request.isObjectJQuery(urlOrElement) &&
                urlOrElement.is("form, input, select") &&
                this.isFunction($.fn.ajaxForm)
            ){
                var form = urlOrElement.is("form") ? urlOrElement : urlOrElement.parents("form").first();
                form.ajaxSubmit(ajaxOptions);
            } else {
                return $.ajax(ajaxOptions);
            }
        }
        return true;
    };

    // Die URL fuer den Request suchen
    Request.prototype.getUrl = function(urlOrElement) {
        var url = urlOrElement;
        if (this.isObjectJQuery(urlOrElement)) {
            // Wir haben einen Link und nutzen dessen href
            if (urlOrElement.is("a")) {
                url = urlOrElement.get(0).href;
            }
            // Wir haben ein Formular, und besorgen uns dessen action
            else if(urlOrElement.is("form, input, select")) {
                var form = urlOrElement.is("form") ? urlOrElement : urlOrElement.parents("form").first(),
                    href = form.is("form") ? form.prop("action") : url
                ;
                url = href;
            }
        }
        // what todo, if no url was found? use w.location.href?
        return url;
    };
    // Alle Parameter fuer den Request zusammen suchen.
    Request.prototype.prepareParameters = function(urlOrElement, parameters) {
        var _request = this;
        var indexesByParameters = [];
        // Die URL fuer den Request bauen
        if (!_request.isDefined(parameters.href)) {
            parameters.href = _request.getUrl(urlOrElement);
        }
        if (_request.isObjectJQuery(urlOrElement)) {
            if(urlOrElement.is("form, input, select")) {
                var form = urlOrElement.is("form") ? urlOrElement : urlOrElement.parents("form").first(),
                    isGet = form.attr("method").toLowerCase() === "get",
                    params = form.serializeArray(),
                    submitName = urlOrElement.is("input[type=submit]") ? urlOrElement.prop("name") : false;

                // Parameter des Formulars sammeln
                var isFirstParameter = true;
                $.each(params, function(index, object){
                    if (isGet) {
                        var parameterGlue = '&';
                        if (isFirstParameter && parameters.href.indexOf("?") == -1) {
                            parameterGlue = '?';
                        }
                        parameters.href += parameterGlue + object.name + "=" + object.value;
                    } else if (!_request.isDefined(parameters[object.name])) {
                        // The [] at the end of the parameter name means we have a multi-select or multi-checkbox
                        // without dedicated indexes for each option like tx_news_pi1[search][articletype][]
                        // if multiple options have been selected only the first one get's
                        // added to the parameter array as the object.name would be the same
                        // for all options if they don't have indexes.
                        // That's why we insert an index to make sure every option has it's own index
                        // and unique parameter name like
                        // tx_news_pi1[search][articletype][9], tx_news_pi1[search][articletype][10] etc.
                        // We mimic the behaviour of browsers and start the index per parameter
                        // at 0 and increment step by step.
                        // @todo use object.name.endsWith('[]') when support for browsers
                        // without ECMAScript 6 is dropped.
                        if (object.name.substring(object.name.length - 2, object.name.length) === "[]") {
                            if (!_request.isDefined(indexesByParameters[object.name])) {
                                indexesByParameters[object.name] = 0;
                            } else {
                                indexesByParameters[object.name]++;
                            }
                            object.name = object.name.replace("[]", '[' + indexesByParameters[object.name] + ']')
                        }
                        parameters[object.name] = object.value;
                    }
                    isFirstParameter = false;
                });
                // Den Wert des aktuellen Submit-Buttons mitsenden!
                if (_request.isString(submitName) && submitName.length > 0) {
                    if (isGet) {
                        parameters.href += "&" + submitName + "=" + urlOrElement.prop("value");
                    } else if (typeof object != 'undefined' && !_request.isDefined(parameters[object.name])) {
                        parameters[submitName] = urlOrElement.prop("value");
                    }
                }

                parameters.requestType = isGet ? 'GET' : 'POST';
            } else {
                parameters.requestType = urlOrElement.hasClass('ajax-get-request') ? 'GET' : 'POST';
            }
        }
        return parameters;
    };
    // Wir erzeugen einen loader, fuer den asyncronen Call.
    Request.prototype.getLoader = function() {
        var $loader = $('body > .waiting');
        // Nix da? Wir bauen einen neuen!
        if ($loader.length === 0) {
            $loader = $("<div>").addClass("waiting");
            $('body').prepend($loader.hide());
        }
        return $loader;
    };
    // Liefert den Cache
    Request.prototype.getCache = function() {
        return DMK.Registry;
    };
    // Wird beim Start des Calls aufgerufen
    Request.prototype.handleHistoryOnSuccess = function(parameters) {
        // browser url anpassen?
        if (
            this.isDefined(parameters.useHistory)
            && parameters.useHistory
            && DMK.Base.isObject(DMK.History)
        ) {
            DMK.History.setHistoryUrl(parameters.href);
        }
    };
    // Wird beim Start des Calls aufgerufen
    Request.prototype.onStart = function(data, parameters) {
        this.getLoader().show();
    };
    // Wird bei erfolgreichem Call aufgerufen
    Request.prototype.onSuccess = function(data, parameters, textStatus, jqXHR) {};
    // Wird im Fehlerfall ausgerufen
    Request.prototype.onFailure = function(data, parameters, jqXHR, textStatus, errorThrown) {};
    // Wird immer nach onStart nach abschluss eines Calls aufgerufen
    Request.prototype.onComplete = function(data, parameters, jqXHR, textStatus) {
        if (typeof jqXHR !== "undefined" && jqXHR.getResponseHeader('Mktools_Location') !== null) {
            // @todo make it possible to open the location in a new tab instead of the current one.
            window.location = jqXHR.getResponseHeader('Mktools_Location');
        } else {
            this.getLoader().hide();
        }
    };

    // add lib to basic library
    DMK.Libraries.add(Request);

})(DMK, window, jQuery);

/**
 * AjaxContent
 *
 * Typo3 Ajax-Content Lib.
 *
 * Fuehrt automatisch einen Ajax call fuer bestimmte Links oder Formulare durch.
 * Dabei wird automatisch die ContentId ermittelt und genau dieses Element
 * neu gerendert und ersetzt.
 *
 * @copyright Copyright (c) 2014 DMK E-BUSINESS GmbH <dev@dmk-ebusiness.de>
 * @license http://www.gnu.org/licenses/lgpl.html
 *             GNU Lesser General Public License, version 3 or later
 * @version 0.1.0
 * @requires jQuery, Base, Request
 * @author Michael Wagner <michael.wagner@dmk-ebusiness.de>
 */
/*
 * Sample to set the PageType:
 * DMK.AjaxContent.setData("pageType", 99);
 */
/*
 * Sample to override the RequestCall:
 *     DMK.Objects.AjaxContentAjaxRequest.prototype.onSuccess = function(data, parameters) {
 *         // do some thinks
 *     }
 */
(function(DMK, w, $){
    "use strict";
    var AjaxRequest, AjaxContent, VERSION = "0.1.0";


    AjaxContent = function AjaxContent() {
        this.setData("version", VERSION);
        this.setData("pageType", 9267);
    };

    // Ajax Request definieren
    AjaxRequest = DMK.Request.extend(function AjaxRequest() {});
    AjaxRequest.prototype.getLoader = function() { return $(); };

    // wir erben von dem basis objekt
    AjaxContent = DMK.Base.extend(AjaxContent);


    AjaxContent.prototype.init = function() {
        var _self = this,
            _event = function(event, element) {
                return _self.handleAjaxClick(event, element);
            }
        ;
        // click events
        $("body")
            .off("click.ajaxcontentlinks")
            .on(
                "click.ajaxcontentlinks",
                ".ajax-links a, a.ajax-link",
                _event
            );
        // submit events
        $("body")
            .off("submit.ajaxcontentform")
            .on(
                "submit.ajaxcontentform",
                "form.ajax-form",
                _event
            );
        // autotriger for forms
        $("body")
            .off("click.ajaxcontentform")
            .on(
                "click.ajaxcontentform",
                "form.ajax-autotrigger input:not(:text)",
                _event
            )
            .off("change.ajaxcontentform")
            .on(
                "change.ajaxcontentform",
                "form.ajax-autotrigger select",
                _event
            );

        $('.ajax-links-autoload').each(function() {
            _self.handleAjaxClick(null, $(this)[0]);
        });
    };

    AjaxContent.prototype.handleAjaxClick = function(event, element) {
        var _self = this, _request = DMK.Objects.getInstance("AjaxContentAjaxRequest"),
            parameters = {type : this.getData("pageType")},
            $el, $linkwrap, $content,
            xhr
        ;

        element = _self.isDefined(element) ? element : event.target;

        // do request only if there is no target attribute
        if (element.tagName.toLowerCase() === "a") {
            if (
                element.target.length > 0
                || (
                    element.href.search(':') >= 0
                    && $.inArray(
                        element.href.split(':').shift().toLowerCase(),
                        // complete list of available schemes:
                        // http://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
                        ["javascript", "mailto", "tel", "fax", "about", "data"]
                    ) >= 0
                )
            ) {
                return;
            }
        }

        $el = $(element);

        if ($el.hasClass("ajax-autotrigger-ignore")) {
            return;
        }

        // wir suchen die contentid! (e.g. id="c516")
        if ($el.data("ajaxreplaceid") && _self.isNumeric($el.data("ajaxreplaceid").slice(1))) {
            $content = $el;
        }

        if (_self.isDefined($content)) {
            // Abbruch bei nicht vorhandenem Element
            $content = $("#" + $content.data("ajaxreplaceid"));
            if (typeof $content === "undefined") {
                return false;
            }
        }
        else {
            $el.parents("div[id^='c'], section[id^='c'], article[id^='c']").each(
                function(index, element) {
                    $content = $(element);
                    if (_self.isNumeric($content.attr("id").slice(1))) {
                        return false;
                    }
                    return true;
                }
            );
        }
        // kein content element gefunden, wir ersetzen nichts!
        if (!_self.isObjectJQuery($content) || $content.length === 0) {
            return ;
        }

        $content.addClass("ajax-content");
        if ($content.find(".waiting").length === 0) {
            $content.append($("<div>").addClass("waiting").hide());
        }

        // ajax parameter sammeln
        if ($content.data("ajaxreplaceid")) {
            parameters.contentid = $content.data("ajaxreplaceid").slice(1);
        }
        else {
            parameters.contentid = $content.attr("id").slice(1);
        }

        // so we know in DMK\Mktools\ContentObject\UserInternalContentObject
        // that the content should be rendered for real
        parameters.mktoolsAjaxRequest = true;

        $linkwrap = $el.parent();
        if ($linkwrap.hasClass("ajax-link-next") || $linkwrap.hasClass("browse_next")) {
            parameters.page = "next";
        }
        else if ($linkwrap.hasClass("ajax-link-prev") || $linkwrap.hasClass("browse_prev")) {
            parameters.page = "prev";
        }
        else {
            // try to fetch page
            parameters.page = "";
        }

        // decide whether action url is written in history or not
        if (!$el.hasClass("ajax-no-history")) {
            parameters.useHistory = true;
        }

        // die events anlegen
        _request.onStart = function(data, parameters){
            this.parent().onStart.call(this, data, parameters);
            $content.find(".waiting").clearQueue().fadeIn();
        };
        _request.onComplete = function(data, parameters, jqXHR, textStatus){
            this.parent().onComplete.call(this, data, parameters, jqXHR, textStatus);
            $content.find(".waiting").clearQueue().fadeOut();
        };
        _request.onSuccess = function(data, parameters, textStatus, jqXHR) {
            this.parent().onSuccess.call(this, data, parameters, textStatus, jqXHR);
            var from = 0, to = 0;
            if (parameters.page === "next") {
                to = 1;
            }
            else if (parameters.page === "prev") {
                from = 1;
            }
            _self.replaceContent(parameters.contentid, data, from, to);
        };

        if ($el.hasClass("notcachable")) {
            _request.getCache = function() {
                return false;
            };
        }

        if ((xhr = _request.doCall($el, parameters)) && event) {
            event.preventDefault();
        }
        return xhr;
    };

    AjaxContent.prototype.replaceContent = function(contentId, html, from, to) {
        var $cOld = $("#c" + contentId), $cNew,
            animateTime = 1000;
        if ($cOld.length === 0) {
            return;
        }
        // wir sliden
        if (from != to) {
            var $slidebox = $cOld.parent(),
                left = from < to, // true > slide to left, false > slide to right
                old = { width : $cOld.width(), height : $cOld.height() };
            // slidebox wrap erzeugen, falls nicht existent.
            if (!$slidebox.hasClass("ajax-wrap")) {
                $cOld.wrap($("<div>").addClass("ajax-wrap"));
                $slidebox = $cOld.parent();
                $slidebox.css({"position" : "relative", "overflow" : "hidden"});
            }
            $slidebox.width(old.width).height(old.height);
            $cOld.css({"position" : "absolute", "top" : 0, "left" : 0, "width" : old.width});
            // das alte element
            $cOld.attr('id', contentId + '-old').addClass("ajax-content-old");
            $cNew = left ? $(html).insertAfter($cOld) : $(html).insertBefore($cOld);
            if ($cNew.length === 0) {
                from = to = 0;
            }
            else {
                $cNew.css({"position" : "absolute", "top" : 0, "left" : old.width * (left ? +1 : -1), "width" : old.width});
                $slidebox.find(".waiting").clearQueue().fadeOut();
                $slidebox.animate({"height" : $cNew.height()}, animateTime);
                $cOld.animate({"left" : old.width * (left ? -1 : +1)}, animateTime);
                $cNew.animate({"left" : 0}, animateTime, function () {
                    w.setTimeout(function() {
                        $cOld.remove();
                    }, 250);
                });
            }

        }

        // normales ersetzen
        if (from == to) {
            $cOld.replaceWith(html);
        }

        this.onAfterReplaceContent(contentId);
    };

    /**
     * Overwrite this method if you want to do something after the content is replaced
     */
    AjaxContent.prototype.onAfterReplaceContent = function(contentId) {
    };

    // add lib to basic library
    DMK.Objects.add(AjaxRequest, "AjaxContentAjaxRequest");
    DMK.Libraries.add(AjaxContent);
})(DMK, window, jQuery);
