<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
/* $Id$ */

cocoon.load("resource://org/apache/cocoon/forms/flow/javascript/Form.js");
importPackage(Packages.org.apache.lenya.cms.linking)

//placeholders for custom flow code:
var customLoopFlow = undefined;
var customSubmitFlow = undefined;

/**
 * Get usecase.
 *
 * @param usecaseName, a string
 * @return a new org.apache.lenya.cms.usecase.Usecase Avalon component
 */
function getUsecase(usecaseName) {
    var flowHelper;
    var request;
    var sourceUrl;
    var usecaseResolver;
    var usecase;
    try {
        flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper");
        request = flowHelper.getRequest(cocoon);
        sourceUrl = Packages.org.apache.lenya.util.ServletHelper.getWebappURI(request);
        usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
        usecase = usecaseResolver.resolve(sourceUrl, usecaseName);
        usecase.setSourceURL(sourceUrl);
        usecase.setName(usecaseName);
    } catch (exception) {
        log("error", "Error in getUsecase(): " + exception);
        log("debug", "usecaseName = " + usecaseName);
        log("debug", "flowHelper = " + flowHelper);
        log("debug", "request = " + request);
        log("debug", "sourceUrl = " + sourceUrl);
        log("debug", "usecaseResolver = " + usecaseResolver);
        log("debug", "usecase = " + usecase);
        throw exception;
    } finally {
        cocoon.releaseComponent(flowHelper);
        cocoon.releaseComponent(usecaseResolver);
    }
    return usecase;
}

/**
 * Release usecase. Since usecases are Avalon Components, they must
 * be released before a continuation is created.
 *
 * @param usecase, a org.apache.lenya.cms.usecase.Usecase Avalon component
 */
function releaseUsecase(usecase) {
    var usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
    try {
        usecaseResolver.release(usecase);
    } finally {
        cocoon.releaseComponent(usecaseResolver);
    }
}

/**
 * Pass all parameters from the current request to a usecase
 * (except lenya.usecase, lenya.continuation and submit).
 *
 * @param usecase, a org.apache.lenya.cms.usecase.Usecase Avalon component
 */
function passRequestParameters(usecase) {
    var flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper");
    var names = cocoon.request.getParameterNames();
    while (names.hasMoreElements()) {
        var name = names.nextElement();
        // some parameters are handled elsewhere:
        if (!name.equals("lenya.usecase")
            && !name.equals("lenya.continuation")
            && !name.equals("submit")) {
            // pass the rest on:
            var value = flowHelper.getRequest(cocoon).get(name);
            var string = new Packages.java.lang.String();
            var vector = new Packages.java.util.Vector();
            if (string.getClass().isInstance(value) || vector.getClass().isInstance(value)) {
                // use getParameters() to avoid character encoding problems
                var values = flowHelper.getRequest(cocoon).getParameterValues(name);
                if (values.length < 2) {
                    usecase.setParameter(name, values[0]);
                } else {
                    usecase.setParameter(name, values);
                }
            } else if (value == null) {
                value = cocoon.request.getParameter(name);
                usecase.setParameter(name, value);
            } else {
                usecase.setPart(name, value);
            }
        }
    }
    cocoon.releaseComponent(flowHelper);
}

/**
 * Load the custom flow functions as provided in the view
 * configuration, if any.
 *
 * @param view, a org.apache.lenya.cms.usecase.UsecaseView object
 */
function loadCustomFlow(view) {
    customLoopFlow = undefined;
    customSubmitFlow = undefined;

    var flowUri;
    if (view != null) {
        flowUri = view.getCustomFlow();
        if (flowUri != null && flowUri != "") { // for some reason, flowUri is not correctly cast into a Boolean, so "if (flowUri)" does not work
            log("debug", "customFlow uri: [" + flowUri + "]");
            cocoon.load(flowUri);
        }
    } else {
        log("debug", "Usecase does not define a view.");
    }
}

/**
 * Log messages via cocoon.log.
 *
 * @param level, one of ("debug"|"info"|"warn"|"error")
 * @param message, a string
 * @param usecaseName, a string.
 */
function log(level, message, usecaseName) {
    var msg = "usecases.js::executeUsecase() "
        + (usecaseName ? "with lenya.usecase=[" + usecaseName + "]" : "")
        + ": "
        + message;
    switch (level) {
        case "debug":
            if (cocoon.log.isDebugEnabled())
                cocoon.log.debug(msg);
            break;
        case "info":
            cocoon.log.info(msg);
            break;
       case "warn":
            cocoon.log.warn(msg);
            break;
        case "error":
            cocoon.log.error(msg);
            break;
        default:
            cocoon.log.error(msg + "[Unknown log level " + level + "]");
            break;
    }
}

/**
 * The Loop stage of the flow, in which a view is displayed.
 * Note: All Avalon components should be released before calling
 * this function! This means that you cannot hold a usecase object,
 * hence the proxy.
 *
 * @param view, a org.apache.lenya.cms.usecase.UsecaseView object
 * @param proxy, a org.apache.lenya.cms.usecase.UsecaseProxy object
 * @param generic, a generic Javascript object for custom flow code to preserve state information (currently not used by the default code)
 *
 * This function invokes customLoopFlow if it exists.
 * Otherwise it falls back to defaultLoopFlow.
 */
function loopFlow(view, proxy, generic) {
    if (customLoopFlow != undefined) {
        log("info", "Using customLoopFlow function", proxy.getName());
        return customLoopFlow(view, proxy, generic);
    } else{
        return defaultLoopFlow(view, proxy);
    }
}

/**
 * The Submit stage of the flow, in which a user interaction is processed.
 * and the usecase is advanced. If the user has submitted, the usecase is executed.
 *
 * @param usecase, a org.apache.lenya.cms.usecase.Usecase Avalon component
 * @param generic, a generic Javascript object for custom flow code to preserve state information (currently not used by the default code)
 * @return a string with the return state ("success"|"cancel"|"continue").
 *
 * This function invokes customSubmitFlow if it exists.
 * Otherwise it falls back to defaultSubmitFlow.
 */
function submitFlow(usecase, generic) {
    if (customSubmitFlow != null) {
        log("info", "Using customSubmitFlow function", usecase.getName());
        return customSubmitFlow(usecase, generic);
    } else{

        return defaultSubmitFlow(usecase);
    }
}

/**
 * @see loopFlow.
 */
function defaultLoopFlow(view, proxy) {
    var viewUri = view.getViewURI();
    // we used to allow a cocoon:/ and cocoon:// prefix (which sendPageXXX does not handle),
    // but it is now deprecated!
    if (viewUri.startsWith("cocoon:/")) {
        // leave leading / in case of cocoon:// for root sitemap
        viewUri = viewUri.substring(new Packages.java.lang.String("cocoon:/").length());
        log("warn", "The use of the cocoon:/ protocol prefix in the <view uri=\"...\"> attribute is deprecated!");
    }
    if (! viewUri.startsWith("/")) {
        // a local URI must be handled by usecase.xmap, which assumes a prefix "usecases-view/[menu|nomenu]/
        // that determines whether the menu is to be displayed. this mechanism is used by most lenya core usecases.
        viewUri = "usecases-view/"
            + (view.showMenu() ? "menu" : "nomenu")
            + "/" + viewUri;
    }
    if (view.createContinuation()) {
        log("debug", "Creating view and continuation, calling Cocoon with viewUri = [" + viewUri + "]");
        cocoon.sendPageAndWait(viewUri, { "usecase" : proxy });
    } else {
        log("debug", "Creating view without continuation (!), calling Cocoon with viewUri = [" + viewUri + "]");
        cocoon.sendPage(viewUri, { "usecase" : proxy});
        cocoon.exit(); // we're done.
    }
}

/**
 * @see submitFlow
 */
function defaultSubmitFlow(usecase) {
    var preconditionsOk = true;
    if (cocoon.request.getParameter("submit")||cocoon.request.getParameter("lenya.submit")=="ok") {
        if (usecase.isOptimistic()) {
            usecase.checkPreconditions();
            preconditionsOk = !usecase.hasErrors();
        }
        if (preconditionsOk) {
            if (usecase.isOptimistic()) {
                usecase.lockInvolvedObjects();
            }
            usecase.checkExecutionConditions();
            if (! usecase.hasErrors()) {
                return executeFlow(usecase);
            }
        }
    } else if (cocoon.request.getParameter("cancel")) {
        usecase.cancel();
        return "cancel";
    }
    else {
        usecase.advance();
    }
    return "continue"
}

/**
 * The Execute stage of the flow, in which the usecase is finally executed.
 *
 * @param a org.apache.lenya.cms.usecase.Usecase object
 * @return a string with the return state ("success"|"continue").
 */
function executeFlow(usecase) {
    usecase.execute();
    if (! usecase.hasErrors()) {
        usecase.checkPostconditions();
        if (! usecase.hasErrors()) {
            return "success";
        }
    }
    return "continue";
}

/**
 * Redirect to target URL after finishing the usecase,
 * taking proxy settings into account.
 *
 * @param the webapp-internal target URL
 */
function redirect(targetUrl) {
    var flowHelper;  // needed to obtain the current objectModel
    var objectModel; // needed to provide context to the proxyModule
    var inputModuleSelector; // needed to obtain a proxyModule
    var proxyModule; // used to rewrite the targetUrl according to the publication's proxy settings
    var proxyUrl;    // the rewritten target Url

    flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper");
    try {
        objectModel = flowHelper.getObjectModel(cocoon);
    } finally {
        cocoon.releaseComponent(flowHelper);
    }

    inputModuleSelector = cocoon.getComponent(org.apache.cocoon.components.modules.input.InputModule.ROLE + "Selector");
    try {
        proxyModule = inputModuleSelector.select("proxy");
        try {
            proxyUrl = proxyModule.getAttribute(targetUrl, null, objectModel);
        } finally {
            cocoon.releaseComponent(proxyModule);
        }
    } finally {
        cocoon.releaseComponent(inputModuleSelector);
    }

    log("debug", "Redirecting to {proxy:" + targetUrl + "} = " + proxyUrl + ".");

    cocoon.redirectTo(proxyUrl, true);
}

/**
 * Main function to execute a usecase. This is called from <map:flow/>.
 *
 * Uses request parameter "lenya.usecase" to determine what
 * usecase to execute.
 *
 * Since "usecase" and "flowHelper" are avalon components,
 * they must be released before a continuation is created.
 * In order to preserve state information, a "proxy" object
 * is used.
 */
function executeUsecase() {

    var usecaseName;
    var usecase; // the Usecase object
    var proxy; // a UsecaseProxy to make the usecase state persistent across continuations
    var view; // the UsecaseView object that belongs to our usecase.
    var state; // the state of the usecase ("continue"|"success"|"cancel");
    var targetUrl; // URL to redirect to after completion.
    var generic = new Object; // a generic helper object for custom flow code to preserve state information.

    var preconditionsOK;

    try {
        usecaseName = cocoon.parameters["usecaseName"];
        usecase = getUsecase(usecaseName);
        passRequestParameters(usecase);
        usecase.checkPreconditions();
        preconditionsOK = !usecase.hasErrors();
        if (preconditionsOK && !usecase.isOptimistic()) {
            usecase.lockInvolvedObjects();
        }
        // create proxy object to save usecase state
        proxy = new Packages.org.apache.lenya.cms.usecase.impl.UsecaseProxy(usecase);
        view = usecase.getView();
        log("debug", "Successfully prepared usecase.", usecaseName);
    } catch (exception) {
        log("error", "Could not prepare usecase: " + exception, usecaseName);
        throw exception;
    } finally {
        releaseUsecase(usecase);
    }
    loadCustomFlow(view);
    // If the usecase has a view uri, this means we want to display something
    // to the user before proceeding. This also means the usecase can consist
    // of several steps; repeated until the user chooses to submit or cancel.
    if (view != null && view.getViewURI()) {
        do {
            // show the view:
            try {
                loopFlow(view, proxy, generic); //usecase must be released here!
            } catch (exception) {
                // if something went wrong, try and rollback the usecase:
                log("error", "Exception during loopFlow(): " + exception, usecaseName);
                try {
                    usecase = getUsecase(usecaseName);
                    proxy.setup(usecase);
                    usecase.cancel();
                    throw exception;
                } finally {
                    releaseUsecase(usecase);
                }
            }
            if (preconditionsOK) {
                log("debug", "Advancing in usecase.", usecaseName);
                // restore the usecase state and handle the user input:
                usecase = getUsecase(usecaseName);
                proxy.setup(usecase);
                passRequestParameters(usecase);
                state = submitFlow(usecase, generic);
                // create a new proxy with the updated usecase state
                proxy = new Packages.org.apache.lenya.cms.usecase.impl.UsecaseProxy(usecase);
                releaseUsecase(usecase);
            }
        } while (state == "continue");
    // If the usecase does not have a view uri, we can directly jump to
    // executeFlow().
    } else {
        usecase = getUsecase(usecaseName);
        proxy.setup(usecase);
        passRequestParameters(usecase);

        if (usecase.isOptimistic()) {
            usecase.checkPreconditions();
            preconditionsOK = !usecase.hasErrors();
        }

        if (preconditionsOK) {
            if (usecase.isOptimistic()) {
                usecase.lockInvolvedObjects();
            }
            usecase.checkExecutionConditions();
            var hasErrors = usecase.hasErrors();
            if (!hasErrors) {
                state = executeFlow(usecase);
                hasErrors = usecase.hasErrors();
            }
        }
        releaseUsecase(usecase);
        if (hasErrors) {
            proxy = new Packages.org.apache.lenya.cms.usecase.impl.UsecaseProxy(usecase);
            cocoon.sendPage("usecases-view/nomenu/modules/usecase/templates/error.jx", { "usecase" : proxy});
            return;
        }
    }
    //getTargetURL takes a boolean that is true on success:
    targetUrl = usecase.getTargetURL(state == "success");
    log("debug", "Completed, redirecting to url = [" + targetUrl + "]", usecaseName);
    // jump to the appropriate URL:
    redirect(targetUrl);
}
<commit_msg>Call usecase.getView() before building proxy object so that prepareView() has an effect.<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
/* $Id$ */

cocoon.load("resource://org/apache/cocoon/forms/flow/javascript/Form.js");
importPackage(Packages.org.apache.lenya.cms.linking)

//placeholders for custom flow code:
var customLoopFlow = undefined;
var customSubmitFlow = undefined;

/**
 * Get usecase.
 *
 * @param usecaseName, a string
 * @return a new org.apache.lenya.cms.usecase.Usecase Avalon component
 */
function getUsecase(usecaseName) {
    var flowHelper;
    var request;
    var sourceUrl;
    var usecaseResolver;
    var usecase;
    try {
        flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper");
        request = flowHelper.getRequest(cocoon);
        sourceUrl = Packages.org.apache.lenya.util.ServletHelper.getWebappURI(request);
        usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
        usecase = usecaseResolver.resolve(sourceUrl, usecaseName);
        usecase.setSourceURL(sourceUrl);
        usecase.setName(usecaseName);
    } catch (exception) {
        log("error", "Error in getUsecase(): " + exception);
        log("debug", "usecaseName = " + usecaseName);
        log("debug", "flowHelper = " + flowHelper);
        log("debug", "request = " + request);
        log("debug", "sourceUrl = " + sourceUrl);
        log("debug", "usecaseResolver = " + usecaseResolver);
        log("debug", "usecase = " + usecase);
        throw exception;
    } finally {
        cocoon.releaseComponent(flowHelper);
        cocoon.releaseComponent(usecaseResolver);
    }
    return usecase;
}

/**
 * Release usecase. Since usecases are Avalon Components, they must
 * be released before a continuation is created.
 *
 * @param usecase, a org.apache.lenya.cms.usecase.Usecase Avalon component
 */
function releaseUsecase(usecase) {
    var usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
    try {
        usecaseResolver.release(usecase);
    } finally {
        cocoon.releaseComponent(usecaseResolver);
    }
}

/**
 * Pass all parameters from the current request to a usecase
 * (except lenya.usecase, lenya.continuation and submit).
 *
 * @param usecase, a org.apache.lenya.cms.usecase.Usecase Avalon component
 */
function passRequestParameters(usecase) {
    var flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper");
    var names = cocoon.request.getParameterNames();
    while (names.hasMoreElements()) {
        var name = names.nextElement();
        // some parameters are handled elsewhere:
        if (!name.equals("lenya.usecase")
            && !name.equals("lenya.continuation")
            && !name.equals("submit")) {
            // pass the rest on:
            var value = flowHelper.getRequest(cocoon).get(name);
            var string = new Packages.java.lang.String();
            var vector = new Packages.java.util.Vector();
            if (string.getClass().isInstance(value) || vector.getClass().isInstance(value)) {
                // use getParameters() to avoid character encoding problems
                var values = flowHelper.getRequest(cocoon).getParameterValues(name);
                if (values.length < 2) {
                    usecase.setParameter(name, values[0]);
                } else {
                    usecase.setParameter(name, values);
                }
            } else if (value == null) {
                value = cocoon.request.getParameter(name);
                usecase.setParameter(name, value);
            } else {
                usecase.setPart(name, value);
            }
        }
    }
    cocoon.releaseComponent(flowHelper);
}

/**
 * Load the custom flow functions as provided in the view
 * configuration, if any.
 *
 * @param view, a org.apache.lenya.cms.usecase.UsecaseView object
 */
function loadCustomFlow(view) {
    customLoopFlow = undefined;
    customSubmitFlow = undefined;

    var flowUri;
    if (view != null) {
        flowUri = view.getCustomFlow();
        if (flowUri != null && flowUri != "") { // for some reason, flowUri is not correctly cast into a Boolean, so "if (flowUri)" does not work
            log("debug", "customFlow uri: [" + flowUri + "]");
            cocoon.load(flowUri);
        }
    } else {
        log("debug", "Usecase does not define a view.");
    }
}

/**
 * Log messages via cocoon.log.
 *
 * @param level, one of ("debug"|"info"|"warn"|"error")
 * @param message, a string
 * @param usecaseName, a string.
 */
function log(level, message, usecaseName) {
    var msg = "usecases.js::executeUsecase() "
        + (usecaseName ? "with lenya.usecase=[" + usecaseName + "]" : "")
        + ": "
        + message;
    switch (level) {
        case "debug":
            if (cocoon.log.isDebugEnabled())
                cocoon.log.debug(msg);
            break;
        case "info":
            cocoon.log.info(msg);
            break;
       case "warn":
            cocoon.log.warn(msg);
            break;
        case "error":
            cocoon.log.error(msg);
            break;
        default:
            cocoon.log.error(msg + "[Unknown log level " + level + "]");
            break;
    }
}

/**
 * The Loop stage of the flow, in which a view is displayed.
 * Note: All Avalon components should be released before calling
 * this function! This means that you cannot hold a usecase object,
 * hence the proxy.
 *
 * @param view, a org.apache.lenya.cms.usecase.UsecaseView object
 * @param proxy, a org.apache.lenya.cms.usecase.UsecaseProxy object
 * @param generic, a generic Javascript object for custom flow code to preserve state information (currently not used by the default code)
 *
 * This function invokes customLoopFlow if it exists.
 * Otherwise it falls back to defaultLoopFlow.
 */
function loopFlow(view, proxy, generic) {
    if (customLoopFlow != undefined) {
        log("info", "Using customLoopFlow function", proxy.getName());
        return customLoopFlow(view, proxy, generic);
    } else{
        return defaultLoopFlow(view, proxy);
    }
}

/**
 * The Submit stage of the flow, in which a user interaction is processed.
 * and the usecase is advanced. If the user has submitted, the usecase is executed.
 *
 * @param usecase, a org.apache.lenya.cms.usecase.Usecase Avalon component
 * @param generic, a generic Javascript object for custom flow code to preserve state information (currently not used by the default code)
 * @return a string with the return state ("success"|"cancel"|"continue").
 *
 * This function invokes customSubmitFlow if it exists.
 * Otherwise it falls back to defaultSubmitFlow.
 */
function submitFlow(usecase, generic) {
    if (customSubmitFlow != null) {
        log("info", "Using customSubmitFlow function", usecase.getName());
        return customSubmitFlow(usecase, generic);
    } else{

        return defaultSubmitFlow(usecase);
    }
}

/**
 * @see loopFlow.
 */
function defaultLoopFlow(view, proxy) {
    var viewUri = view.getViewURI();
    // we used to allow a cocoon:/ and cocoon:// prefix (which sendPageXXX does not handle),
    // but it is now deprecated!
    if (viewUri.startsWith("cocoon:/")) {
        // leave leading / in case of cocoon:// for root sitemap
        viewUri = viewUri.substring(new Packages.java.lang.String("cocoon:/").length());
        log("warn", "The use of the cocoon:/ protocol prefix in the <view uri=\"...\"> attribute is deprecated!");
    }
    if (! viewUri.startsWith("/")) {
        // a local URI must be handled by usecase.xmap, which assumes a prefix "usecases-view/[menu|nomenu]/
        // that determines whether the menu is to be displayed. this mechanism is used by most lenya core usecases.
        viewUri = "usecases-view/"
            + (view.showMenu() ? "menu" : "nomenu")
            + "/" + viewUri;
    }
    if (view.createContinuation()) {
        log("debug", "Creating view and continuation, calling Cocoon with viewUri = [" + viewUri + "]");
        cocoon.sendPageAndWait(viewUri, { "usecase" : proxy });
    } else {
        log("debug", "Creating view without continuation (!), calling Cocoon with viewUri = [" + viewUri + "]");
        cocoon.sendPage(viewUri, { "usecase" : proxy});
        cocoon.exit(); // we're done.
    }
}

/**
 * @see submitFlow
 */
function defaultSubmitFlow(usecase) {
    var preconditionsOk = true;
    if (cocoon.request.getParameter("submit")||cocoon.request.getParameter("lenya.submit")=="ok") {
        if (usecase.isOptimistic()) {
            usecase.checkPreconditions();
            preconditionsOk = !usecase.hasErrors();
        }
        if (preconditionsOk) {
            if (usecase.isOptimistic()) {
                usecase.lockInvolvedObjects();
            }
            usecase.checkExecutionConditions();
            if (! usecase.hasErrors()) {
                return executeFlow(usecase);
            }
        }
    } else if (cocoon.request.getParameter("cancel")) {
        usecase.cancel();
        return "cancel";
    }
    else {
        usecase.advance();
    }
    return "continue"
}

/**
 * The Execute stage of the flow, in which the usecase is finally executed.
 *
 * @param a org.apache.lenya.cms.usecase.Usecase object
 * @return a string with the return state ("success"|"continue").
 */
function executeFlow(usecase) {
    usecase.execute();
    if (! usecase.hasErrors()) {
        usecase.checkPostconditions();
        if (! usecase.hasErrors()) {
            return "success";
        }
    }
    return "continue";
}

/**
 * Redirect to target URL after finishing the usecase,
 * taking proxy settings into account.
 *
 * @param the webapp-internal target URL
 */
function redirect(targetUrl) {
    var flowHelper;  // needed to obtain the current objectModel
    var objectModel; // needed to provide context to the proxyModule
    var inputModuleSelector; // needed to obtain a proxyModule
    var proxyModule; // used to rewrite the targetUrl according to the publication's proxy settings
    var proxyUrl;    // the rewritten target Url

    flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper");
    try {
        objectModel = flowHelper.getObjectModel(cocoon);
    } finally {
        cocoon.releaseComponent(flowHelper);
    }

    inputModuleSelector = cocoon.getComponent(org.apache.cocoon.components.modules.input.InputModule.ROLE + "Selector");
    try {
        proxyModule = inputModuleSelector.select("proxy");
        try {
            proxyUrl = proxyModule.getAttribute(targetUrl, null, objectModel);
        } finally {
            cocoon.releaseComponent(proxyModule);
        }
    } finally {
        cocoon.releaseComponent(inputModuleSelector);
    }

    log("debug", "Redirecting to {proxy:" + targetUrl + "} = " + proxyUrl + ".");

    cocoon.redirectTo(proxyUrl, true);
}

/**
 * Main function to execute a usecase. This is called from <map:flow/>.
 *
 * Uses request parameter "lenya.usecase" to determine what
 * usecase to execute.
 *
 * Since "usecase" and "flowHelper" are avalon components,
 * they must be released before a continuation is created.
 * In order to preserve state information, a "proxy" object
 * is used.
 */
function executeUsecase() {

    var usecaseName;
    var usecase; // the Usecase object
    var proxy; // a UsecaseProxy to make the usecase state persistent across continuations
    var view; // the UsecaseView object that belongs to our usecase.
    var state; // the state of the usecase ("continue"|"success"|"cancel");
    var targetUrl; // URL to redirect to after completion.
    var generic = new Object; // a generic helper object for custom flow code to preserve state information.

    var preconditionsOK;

    try {
        usecaseName = cocoon.parameters["usecaseName"];
        usecase = getUsecase(usecaseName);
        passRequestParameters(usecase);
        usecase.checkPreconditions();
        preconditionsOK = !usecase.hasErrors();
        if (preconditionsOK && !usecase.isOptimistic()) {
            usecase.lockInvolvedObjects();
        }
        // create proxy object to save usecase state
        view = usecase.getView();
        proxy = new Packages.org.apache.lenya.cms.usecase.impl.UsecaseProxy(usecase);
        log("debug", "Successfully prepared usecase.", usecaseName);
    } catch (exception) {
        log("error", "Could not prepare usecase: " + exception, usecaseName);
        throw exception;
    } finally {
        releaseUsecase(usecase);
    }
    loadCustomFlow(view);
    // If the usecase has a view uri, this means we want to display something
    // to the user before proceeding. This also means the usecase can consist
    // of several steps; repeated until the user chooses to submit or cancel.
    if (view != null && view.getViewURI()) {
        do {
            // show the view:
            try {
                loopFlow(view, proxy, generic); //usecase must be released here!
            } catch (exception) {
                // if something went wrong, try and rollback the usecase:
                log("error", "Exception during loopFlow(): " + exception, usecaseName);
                try {
                    usecase = getUsecase(usecaseName);
                    proxy.setup(usecase);
                    usecase.cancel();
                    throw exception;
                } finally {
                    releaseUsecase(usecase);
                }
            }
            if (preconditionsOK) {
                log("debug", "Advancing in usecase.", usecaseName);
                // restore the usecase state and handle the user input:
                usecase = getUsecase(usecaseName);
                proxy.setup(usecase);
                passRequestParameters(usecase);
                state = submitFlow(usecase, generic);
                // create a new proxy with the updated usecase state
                proxy = new Packages.org.apache.lenya.cms.usecase.impl.UsecaseProxy(usecase);
                releaseUsecase(usecase);
            }
        } while (state == "continue");
    // If the usecase does not have a view uri, we can directly jump to
    // executeFlow().
    } else {
        usecase = getUsecase(usecaseName);
        proxy.setup(usecase);
        passRequestParameters(usecase);

        if (usecase.isOptimistic()) {
            usecase.checkPreconditions();
            preconditionsOK = !usecase.hasErrors();
        }

        if (preconditionsOK) {
            if (usecase.isOptimistic()) {
                usecase.lockInvolvedObjects();
            }
            usecase.checkExecutionConditions();
            var hasErrors = usecase.hasErrors();
            if (!hasErrors) {
                state = executeFlow(usecase);
                hasErrors = usecase.hasErrors();
            }
        }
        releaseUsecase(usecase);
        if (hasErrors) {
            proxy = new Packages.org.apache.lenya.cms.usecase.impl.UsecaseProxy(usecase);
            cocoon.sendPage("usecases-view/nomenu/modules/usecase/templates/error.jx", { "usecase" : proxy});
            return;
        }
    }
    //getTargetURL takes a boolean that is true on success:
    targetUrl = usecase.getTargetURL(state == "success");
    log("debug", "Completed, redirecting to url = [" + targetUrl + "]", usecaseName);
    // jump to the appropriate URL:
    redirect(targetUrl);
}
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

'use strict';

var interop_server = require('../interop/interop_server.js');
var interop_client = require('../interop/interop_client.js');

var server;

var port;

var name_override = 'foo.test.google.fr';

describe('Interop tests', function() {
  before(function(done) {
    var server_obj = interop_server.getServer(0, true);
    server = server_obj.server;
    server.start();
    port = 'localhost:' + server_obj.port;
    done();
  });
  after(function() {
    server.forceShutdown();
  });
  // This depends on not using a binary stream
  it('should pass empty_unary', function(done) {
    interop_client.runTest(port, name_override, 'empty_unary', true, true,
                           done);
  });
  // This fails due to an unknown bug
  it('should pass large_unary', function(done) {
    interop_client.runTest(port, name_override, 'large_unary', true, true,
                           done);
  });
  it('should pass client_streaming', function(done) {
    interop_client.runTest(port, name_override, 'client_streaming', true, true,
                           done);
  });
  it('should pass server_streaming', function(done) {
    interop_client.runTest(port, name_override, 'server_streaming', true, true,
                           done);
  });
  it('should pass ping_pong', function(done) {
    interop_client.runTest(port, name_override, 'ping_pong', true, true, done);
  });
  it('should pass empty_stream', function(done) {
    interop_client.runTest(port, name_override, 'empty_stream', true, true,
                           done);
  });
  it('should pass cancel_after_begin', function(done) {
    interop_client.runTest(port, name_override, 'cancel_after_begin', true,
                           true, done);
  });
  it('should pass cancel_after_first_response', function(done) {
    interop_client.runTest(port, name_override, 'cancel_after_first_response',
                           true, true, done);
  });
  it('should pass timeout_on_sleeping_server', function(done) {
    interop_client.runTest(port, name_override, 'timeout_on_sleeping_server',
                           true, true, done);
  });
  it('should pass custom_metadata', function(done) {
    interop_client.runTest(port, name_override, 'custom_metadata',
                           true, true, done);
  });
  it('should pass status_code_and_message', function(done) {
    interop_client.runTest(port, name_override, 'status_code_and_message',
                           true, true, done);
  });
  it('should pass unimplemented_method', function(done) {
    interop_client.runTest(port, name_override, 'unimplemented_method',
                           true, true, done);
  });
});
<commit_msg>added new test to interop_sanity_test.js<commit_after>/*
 *
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

'use strict';

var interop_server = require('../interop/interop_server.js');
var interop_client = require('../interop/interop_client.js');

var server;

var port;

var name_override = 'foo.test.google.fr';

describe('Interop tests', function() {
  before(function(done) {
    var server_obj = interop_server.getServer(0, true);
    server = server_obj.server;
    server.start();
    port = 'localhost:' + server_obj.port;
    done();
  });
  after(function() {
    server.forceShutdown();
  });
  // This depends on not using a binary stream
  it('should pass empty_unary', function(done) {
    interop_client.runTest(port, name_override, 'empty_unary', true, true,
                           done);
  });
  // This fails due to an unknown bug
  it('should pass large_unary', function(done) {
    interop_client.runTest(port, name_override, 'large_unary', true, true,
                           done);
  });
  it('should pass client_streaming', function(done) {
    interop_client.runTest(port, name_override, 'client_streaming', true, true,
                           done);
  });
  it('should pass server_streaming', function(done) {
    interop_client.runTest(port, name_override, 'server_streaming', true, true,
                           done);
  });
  it('should pass ping_pong', function(done) {
    interop_client.runTest(port, name_override, 'ping_pong', true, true, done);
  });
  it('should pass empty_stream', function(done) {
    interop_client.runTest(port, name_override, 'empty_stream', true, true,
                           done);
  });
  it('should pass cancel_after_begin', function(done) {
    interop_client.runTest(port, name_override, 'cancel_after_begin', true,
                           true, done);
  });
  it('should pass cancel_after_first_response', function(done) {
    interop_client.runTest(port, name_override, 'cancel_after_first_response',
                           true, true, done);
  });
  it('should pass timeout_on_sleeping_server', function(done) {
    interop_client.runTest(port, name_override, 'timeout_on_sleeping_server',
                           true, true, done);
  });
  it('should pass custom_metadata', function(done) {
    interop_client.runTest(port, name_override, 'custom_metadata',
                           true, true, done);
  });
  it('should pass status_code_and_message', function(done) {
    interop_client.runTest(port, name_override, 'status_code_and_message',
                           true, true, done);
  });
  it('should pass unimplemented_service', function(done) {
    interop_client.runTest(port, name_override, 'unimplemented_service',
                           true, true, done);
  });
  it('should pass unimplemented_method', function(done) {
    interop_client.runTest(port, name_override, 'unimplemented_method',
                           true, true, done);
  });
});
<|endoftext|>


<commit_before>(function () {
  'use strict';

  angular
    .module('categorys.admin.routes')
    .config(routeConfig);

  routeConfig.$inject = ['$stateProvider'];

  function routeConfig($stateProvider) {
    $stateProvider
      .state('admin.categorys', {
        url: '/categorys',
        abstract: true,
        template: '<ui-view></ui-view>',
        ncyBreadcrumb: {
          label: 'Categorys'
        }
      })
      .state('admin.categorys.list', {
        url: '/list',
        templateUrl: 'modules/categorys/client/views/admin/list-categorys.client.view.html',
        controller: 'AdminCategorysListController',
        controllerAs: 'vm',
        ncyBreadcrumb: {
          label: 'List'
        }
      })
      .state('admin.categorys.new', {
        url: '/create',
        templateUrl: 'modules/categorys/client/views/admin/form-category.client.view.html',
        controller: 'CategorysController',
        controllerAs: 'vm',
        resolve: {
          categoryResolve: newCategory
        },
        ncyBreadcrumb: {
          label: 'Create'
        }
      })
      .state('admin.categorys.view', {
        url: '/:categoryId',
        templateUrl: 'modules/categorys/client/views/admin/view-category.client.view.html',
        controller: 'CategorysController',
        controllerAs: 'vm',
        resolve: {
          categoryResolve: getCategory
        },
        data: {
          pageTitle: 'View category'
        },
        ncyBreadcrumb: {
          label: 'View'
        }
      })
      .state('admin.categorys.edit', {
        url: '/:categoryId/edit',
        templateUrl: 'modules/categorys/client/views/admin/form-category.client.view.html',
        controller: 'CategoryInputController',
        controllerAs: 'vm',
        resolve: {
          categoryResolve: getCategory
        },
        data: {
          pageTitle: 'Edit {{ categoryResolve.name }}'
        },
        ncyBreadcrumb: {
          label: 'Edit'
        }
      });
  }

  getCategory.$inject = ['$stateParams', 'CategorysService'];

  function getCategory($stateParams, CategorysService) {
    return CategorysService.get({
      categoryId: $stateParams.categoryId
    }).$promise;
  }

  newCategory.$inject = ['CategorysService'];

  function newCategory(CategorysService) {
    return new CategorysService();
  }
}());
<commit_msg>Commited from HaNhatLenh at Sat, Aug 12, 2017  2:28:43 PM<commit_after>(function () {
  'use strict';

  angular
    .module('categorys.admin.routes')
    .config(routeConfig);

  routeConfig.$inject = ['$stateProvider'];

  function routeConfig($stateProvider) {
    $stateProvider
      .state('admin.categorys', {
        url: '/categorys',
        abstract: true,
        template: '<ui-view></ui-view>',
        ncyBreadcrumb: {
          label: 'Categorys'
        }
      })
      .state('admin.categorys.list', {
        url: '/list',
        templateUrl: 'modules/categorys/client/views/admin/list-categorys.client.view.html',
        controller: 'AdminCategorysListController',
        controllerAs: 'vm',
        ncyBreadcrumb: {
          label: 'List'
        }
      })
      .state('admin.categorys.new', {
        url: '/create',
        templateUrl: 'modules/categorys/client/views/admin/form-category.client.view.html',
        controller: 'CategorysController',
        controllerAs: 'vm',
        resolve: {
          categoryResolve: newCategory
        },
        ncyBreadcrumb: {
          label: 'Create'
        }
      })
      .state('admin.categorys.view', {
        url: '/:categoryId',
        templateUrl: 'modules/categorys/client/views/admin/view-category.client.view.html',
        controller: 'CategorysController',
        controllerAs: 'vm',
        resolve: {
          categoryResolve: getCategory
        },
        data: {
          pageTitle: 'View category'
        },
        ncyBreadcrumb: {
          label: 'View'
        }
      })
      .state('admin.categorys.edit', {
        url: '/:categoryId/edit',
        templateUrl: 'modules/categorys/client/views/admin/form-category.client.view.html',
        controller: 'CategorysController',
        controllerAs: 'vm',
        resolve: {
          categoryResolve: getCategory
        },
        data: {
          pageTitle: 'Edit {{ categoryResolve.name }}'
        },
        ncyBreadcrumb: {
          label: 'Edit'
        }
      });
  }

  getCategory.$inject = ['$stateParams', 'CategorysService'];

  function getCategory($stateParams, CategorysService) {
    return CategorysService.get({
      categoryId: $stateParams.categoryId
    }).$promise;
  }

  newCategory.$inject = ['CategorysService'];

  function newCategory(CategorysService) {
    return new CategorysService();
  }
}());
<|endoftext|>


<commit_before>(function () {
  'use strict';
  angular
    .module('polls')
    .controller('PollsListController', PollsListController);

  PollsListController.$inject = [
    '$rootScope',
    '$state',
    '$scope',
    '$window',
    'Socket',
    'Action',
    'toastr',
    'ngDialog',
    'Storages',
    'Constants',
    'UserApi'
  ];

  function PollsListController(
    $rootScope,
    $state,
    $scope,
    $window,
    Socket,
    Action,
    toast,
    dialog,
    Storages,
    Constants,
    UserApi
  ) {
    var vm = this;

    vm.polls = [];
    vm.hot_polls = [];
    vm.bookmarks = [];
    vm.tags = [];
    vm.new_data = [];
    vm.page = 0;
    vm.busy = false;
    vm.stopped = false;
    vm.is_has_new_polls = false;
    init();

    function init() {
      // Lắng nghe sự liện từ socket
      initSocket();
      // Load danh sách tags (Bao gồm số poll)
      get_popular_tags();
      // Load các polls có lượng like nhiều nhất
      get_populars();
      // Load danh sách poll đã bookmark
      if ($scope.isLogged) {
        // Lắng nghe sự kiện từ rootScope;
        get_bookmarks();
      }
    }

    function initSocket() {
      if (!Socket.socket) {
        Socket.connect();
      }
      Socket.emit('subscribe_public');
      Socket.on('poll_create', () => {
        vm.is_has_new_polls = true;
      });
      $scope.$on('$destroy', function () {
        Socket.emit('unsubscribe_public');
        Socket.removeListener('poll_create');
      });
    }

    vm.get_polls = get_polls;
    function get_polls() {
      if (vm.stopped || vm.busy) return;
      vm.busy = true;
      if (Storages.has_session(Constants.storages.polls)) {

      }
      Action.get_polls(vm.page)
        .then(res => {
          if (!res.data.length || res.data.length === 0) {
            vm.stopped = true;
            vm.busy = false;
            return;
          }
          // Xử lý poll trước khi hiển thị màn hình
          var promises = [];
          res.data.forEach(poll => {
            promises.push(process_before_show(poll));
          });
          return Promise.all(promises);
        })
        .then(results => {
          // Gán data vào list hiện tại
          results = results || [];
          vm.polls = _.union(vm.polls, results);
          vm.page += 1;
          vm.busy = false;
        })
        .catch(err => {
          vm.busy = false;
          vm.stopped = true;
          toast.error(err.message, 'Error!');
        });
    }
    function process_before_show(poll) {
      return new Promise((resolve, reject) => {
        poll = Action.process_before_show(poll);
        return resolve(poll);
      });
    }
    function get_populars() {
      Action.get_populars(0)
        .then(res => {
          vm.populars = res.data;
        })
        .catch(err => {
          toast.error(err.message, 'Error!');
        });
    }
    function get_popular_tags() {
      Action.get_popular_tags()
        .then(res => {
          vm.tags = res.data;
        })
        .catch(err => {
          toast.error(err.message, 'Error!');
        });
    }
    function get_bookmarks() {
      UserApi.get_bookmarks($scope.user._id, 0)
        .then(res => {
          vm.bookmarks = res.data || [];
        }, err => {
          toast.error(err.message, 'Error!');
        });
    }

    // Thao tác khác
    $scope.delete_poll = (poll) => {
      if (!poll.isCurrentUserOwner) {
        toast.error('You are not authorized.', 'Error!');
        return;
      }
      $scope.message_title = 'Delete poll!';
      $scope.message_content = 'Are you sure you want to delete this poll?';
      $scope.dialog_type = 3;
      $scope.buton_label = 'delete';
      dialog.openConfirm({
        scope: $scope,
        templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
      }).then(confirm => {
        handle_delete();
      }, reject => {
      });
      function handle_delete() {
        vm.polls = _.without(vm.polls, poll);
        Action.delete_poll(poll);
      }
    };
    $scope.report_poll = (poll) => {
      if (poll.reported) {
        toast.error('You are already reported ' + poll.title, 'Error!');
        return;
      }
      dialog.openConfirm({
        scope: $scope,
        templateUrl: 'modules/core/client/views/templates/report.dialog.template.html'
      }).then(reason => {
        handle_confirm(reason);
      }, reject => {
      });
      function handle_confirm(reason) {
        Action.save_report(poll, reason)
          .then(res => {
            poll.reported = (res) ? true : false;
            toast.success('You have successfully reported ' + poll.title, 'Thank you!');
          })
          .catch(err => {
            toast.error(err.message, 'Error!');
          });
      }
    };
    $scope.bookmark_poll = (poll) => {
      if (poll.bookmarked) {
        toast.error('You are already bookmark ' + poll.title, 'Error!');
        return;
      }
      Action.save_bookmark(poll._id)
        .then(res => {
          poll.bookmarked = (res) ? true : false;
          toast.success('Added ' + poll.title + ' to bookmarks.', 'Success!');
        })
        .catch(err => {
          toast.error(err.message, 'Error!');
        });
    };
    $scope.follow_poll = (poll) => {
      if (!$scope.isLogged) {
        toast.error('You must login to follow poll.', 'Error!');
        return;
      }
      Action.save_follow(poll.follow)
        .then(res => {
          if (res) {
            poll.follow = res;
            toast.success('You followed ' + poll.title, 'Success!');
          } else {
            poll.follow = { poll: poll._id };
          }
        })
        .catch(err => {
          toast.error(err.message, 'Error!');
        });
    };
    $scope.load_new = () => {
      $state.reload();
    };

    // Quick menu
    $scope.isOpen = false;
    $scope.q_quick_poll = () => {
      if (!$scope.isLogged) {
        $state.go('authentication.signin');
        return;
      }
      var q_quick_poll = dialog.open({
        template: '<quick-poll></quick-poll>',
        plain: true,
        controller: 'QuickPollController',
        controllerAs: 'ctrl',
        appendClassName: 'images-upload-dialog',
        closeByDocument: false
      });
    };
    $scope.q_post_poll = () => {
      $state.go('polls.create');
    };
    $scope.q_search = () => {
      $state.go('search');
    };
  }
})();
<commit_msg>Commited from HaNhatLenh at Sun, Aug 27, 2017  1:14:09 PM<commit_after>(function () {
  'use strict';
  angular
    .module('polls')
    .controller('PollsListController', PollsListController);

  PollsListController.$inject = [
    '$rootScope',
    '$state',
    '$scope',
    '$window',
    'Socket',
    'Action',
    'toastr',
    'ngDialog',
    'Storages',
    'Constants',
    'UserApi'
  ];

  function PollsListController(
    $rootScope,
    $state,
    $scope,
    $window,
    Socket,
    Action,
    toast,
    dialog,
    Storages,
    Constants,
    UserApi
  ) {
    var vm = this;

    vm.polls = [];
    vm.hot_polls = [];
    vm.bookmarks = [];
    vm.tags = [];
    vm.new_data = [];
    vm.page = 0;
    vm.busy = false;
    vm.stopped = false;
    vm.is_has_new_polls = false;
    init();

    function init() {
      // Lắng nghe sự liện từ socket
      initSocket();
      // Load danh sách tags (Bao gồm số poll)
      get_popular_tags();
      // Load các polls có lượng like nhiều nhất
      get_populars();
      // Load danh sách poll đã bookmark
      if ($scope.isLogged) {
        // Lắng nghe sự kiện từ rootScope;
        get_bookmarks();
      }
    }

    function initSocket() {
      if (!Socket.socket) {
        Socket.connect();
      }
      Socket.emit('subscribe_public');
      Socket.on('poll_create', () => {
        vm.is_has_new_polls = true;
      });
      $scope.$on('$destroy', function () {
        Socket.emit('unsubscribe_public');
        Socket.removeListener('poll_create');
      });
    }

    vm.get_polls = get_polls;
    function get_polls() {
      if (vm.stopped || vm.busy) return;
      vm.busy = true;
      if (Storages.has_session(Constants.storages.polls)) {

      }
      Action.get_polls(vm.page)
        .then(res => {
          if (!res.data.length || res.data.length === 0) {
            vm.stopped = true;
            vm.busy = false;
            return;
          }
          // Xử lý poll trước khi hiển thị màn hình
          var promises = [];
          res.data.forEach(poll => {
            promises.push(process_before_show(poll));
          });
          return Promise.all(promises);
        })
        .then(results => {
          // Gán data vào list hiện tại
          results = results || [];
          vm.polls = _.union(vm.polls, results);
          vm.page += 1;
          vm.busy = false;
          $scope.$digest();
        })
        .catch(err => {
          vm.busy = false;
          vm.stopped = true;
          toast.error(err.message, 'Error!');
        });
    }
    function process_before_show(poll) {
      return new Promise((resolve, reject) => {
        poll = Action.process_before_show(poll);
        return resolve(poll);
      });
    }
    function get_populars() {
      Action.get_populars(0)
        .then(res => {
          vm.populars = res.data;
        })
        .catch(err => {
          toast.error(err.message, 'Error!');
        });
    }
    function get_popular_tags() {
      Action.get_popular_tags()
        .then(res => {
          vm.tags = res.data;
        })
        .catch(err => {
          toast.error(err.message, 'Error!');
        });
    }
    function get_bookmarks() {
      UserApi.get_bookmarks($scope.user._id, 0)
        .then(res => {
          vm.bookmarks = res.data || [];
        }, err => {
          toast.error(err.message, 'Error!');
        });
    }

    // Thao tác khác
    $scope.delete_poll = (poll) => {
      if (!poll.isCurrentUserOwner) {
        toast.error('You are not authorized.', 'Error!');
        return;
      }
      $scope.message_title = 'Delete poll!';
      $scope.message_content = 'Are you sure you want to delete this poll?';
      $scope.dialog_type = 3;
      $scope.buton_label = 'delete';
      dialog.openConfirm({
        scope: $scope,
        templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
      }).then(confirm => {
        handle_delete();
      }, reject => {
      });
      function handle_delete() {
        vm.polls = _.without(vm.polls, poll);
        Action.delete_poll(poll);
      }
    };
    $scope.report_poll = (poll) => {
      if (poll.reported) {
        toast.error('You are already reported ' + poll.title, 'Error!');
        return;
      }
      dialog.openConfirm({
        scope: $scope,
        templateUrl: 'modules/core/client/views/templates/report.dialog.template.html'
      }).then(reason => {
        handle_confirm(reason);
      }, reject => {
      });
      function handle_confirm(reason) {
        Action.save_report(poll, reason)
          .then(res => {
            poll.reported = (res) ? true : false;
            toast.success('You have successfully reported ' + poll.title, 'Thank you!');
          })
          .catch(err => {
            toast.error(err.message, 'Error!');
          });
      }
    };
    $scope.bookmark_poll = (poll) => {
      if (poll.bookmarked) {
        toast.error('You are already bookmark ' + poll.title, 'Error!');
        return;
      }
      Action.save_bookmark(poll._id)
        .then(res => {
          poll.bookmarked = (res) ? true : false;
          toast.success('Added ' + poll.title + ' to bookmarks.', 'Success!');
        })
        .catch(err => {
          toast.error(err.message, 'Error!');
        });
    };
    $scope.follow_poll = (poll) => {
      if (!$scope.isLogged) {
        toast.error('You must login to follow poll.', 'Error!');
        return;
      }
      Action.save_follow(poll.follow)
        .then(res => {
          if (res) {
            poll.follow = res;
            toast.success('You followed ' + poll.title, 'Success!');
          } else {
            poll.follow = { poll: poll._id };
          }
        })
        .catch(err => {
          toast.error(err.message, 'Error!');
        });
    };
    $scope.load_new = () => {
      $state.reload();
    };

    // Quick menu
    $scope.isOpen = false;
    $scope.q_quick_poll = () => {
      if (!$scope.isLogged) {
        $state.go('authentication.signin');
        return;
      }
      var q_quick_poll = dialog.open({
        template: '<quick-poll></quick-poll>',
        plain: true,
        controller: 'QuickPollController',
        controllerAs: 'ctrl',
        appendClassName: 'images-upload-dialog',
        closeByDocument: false
      });
    };
    $scope.q_post_poll = () => {
      $state.go('polls.create');
    };
    $scope.q_search = () => {
      $state.go('search');
    };
  }
})();
<|endoftext|>


<commit_before>(function () {
  'use strict';
  // Polls controller
  angular
    .module('polls')
    .controller('QuickPollController', QuickPollController);

  QuickPollController.$inject = [
    '$scope',
    '$state',
    'PollsService',
    'Action',
    'Constants'
  ];

  function QuickPollController(
    $scope,
    $state,
    PollsService,
    Action,
    Constants
  ) {
    var ctrl = this;
    ctrl.form = {};
    ctrl.poll = {
      opts: [{
        color: Constants.colors[15]
      }, {
        color: Constants.colors[40]
      }],
      category: $scope.Categorys.list[0]
    };

    ctrl.save = isValid => {
      if (!isValid) {
        $scope.$broadcast('show-errors-check-validity', 'ctrl.form.pollForm');
        return false;
      }

      // if (!ctrl.poll.isPublic) {
      //   $scope.message_title = 'Save poll!';
      //   $scope.message_content = 'You want to save a private poll?';
      //   $scope.dialog_type = 1;
      //   $scope.buton_label = 'Save';
      //   dialog.openConfirm({
      //     scope: $scope,
      //     templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
      //   }).then(confirm => {
      //     handle_save();
      //   }, reject => {
      //   });
      // } else {
      //   handle_save();
      // }
      // function handle_save() {
      //   ctrl.poll.opts = ctrl.opts;
      //   Action.save_poll(ctrl.poll)
      //     .then(res => {
      //       $state.go('polls.view', { pollId: res.slug });
      //     })
      //     .catch(err => {
      //       toast.error(err.message, 'Error!');
      //     });
      // }
    };
    ctrl.add = () => {
      ctrl.poll.opts.push({
        color: randomColor()
      });
    };

    function randomColor() {
      var length = Constants.colors.length;
      var currentColors = _.pluck(ctrl.poll.opts, 'color');
      var color, index;
      do {
        var index = getRandomArbitrary(0, length);
        color = Constants.colors[index];
      } while (!_.contains(currentColors, color));
      return color;
    }
    function getRandomArbitrary(min, max) {
      return Math.random() * (max - min) + min;
    }

  }
})();
<commit_msg>Commited from lenh at Wed, Aug 23, 2017  4:44:23 PM<commit_after>(function () {
  'use strict';
  // Polls controller
  angular
    .module('polls')
    .controller('QuickPollController', QuickPollController);

  QuickPollController.$inject = [
    '$scope',
    '$state',
    'PollsService',
    'Action',
    'Constants'
  ];

  function QuickPollController(
    $scope,
    $state,
    PollsService,
    Action,
    Constants
  ) {
    var ctrl = this;
    ctrl.form = {};
    ctrl.poll = {
      opts: [{
        color: Constants.colors[15]
      }, {
        color: Constants.colors[40]
      }],
      category: $scope.Categorys.list[0]
    };

    ctrl.save = isValid => {
      if (!isValid) {
        $scope.$broadcast('show-errors-check-validity', 'ctrl.form.pollForm');
        return false;
      }

      // if (!ctrl.poll.isPublic) {
      //   $scope.message_title = 'Save poll!';
      //   $scope.message_content = 'You want to save a private poll?';
      //   $scope.dialog_type = 1;
      //   $scope.buton_label = 'Save';
      //   dialog.openConfirm({
      //     scope: $scope,
      //     templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
      //   }).then(confirm => {
      //     handle_save();
      //   }, reject => {
      //   });
      // } else {
      //   handle_save();
      // }
      // function handle_save() {
      //   ctrl.poll.opts = ctrl.opts;
      //   Action.save_poll(ctrl.poll)
      //     .then(res => {
      //       $state.go('polls.view', { pollId: res.slug });
      //     })
      //     .catch(err => {
      //       toast.error(err.message, 'Error!');
      //     });
      // }
    };
    ctrl.add = () => {
      ctrl.poll.opts.push({
        color: randomColor()
      });
    };

    function randomColor() {
      var length = Constants.colors.length;
      var currentColors = _.pluck(ctrl.poll.opts, 'color');
      var color, index;
      do {
        index = getRandomArbitrary(0, length);
        color = Constants.colors[index];
      } while (!_.contains(currentColors, color));
      return color;
    }
    function getRandomArbitrary(min, max) {
      return Math.random() * (max - min) + min;
    }

  }
})();
<|endoftext|>


<commit_before>describe('The dp-straatbeeld-history component', function () {
    var $compile,
        $rootScope,
        $window,
        store,
        ACTIONS;

    beforeEach(function () {
        angular.mock.module(
            'dpStraatbeeld',
            {
                store: {
                    dispatch: angular.noop
                }
            },
            function ($provide) {
                $provide.value('coordinatesFilter', function (input) {
                    return 'MOCKED_RD_COORDINATES (' + input.join(', ') + ')';
                });
            }
        );

        angular.mock.inject(function (_$compile_, _$rootScope_, _$window_, _store_, _ACTIONS_) {
            $compile = _$compile_;
            $rootScope = _$rootScope_;
            $window = _$window_;
            store = _store_;
            ACTIONS = _ACTIONS_;
        });

        spyOn($window, 'open');
        spyOn(store, 'dispatch');
    });

    function getComponent (location, heading, history) {
        const element = document.createElement('dp-straatbeeld-history');
        element.setAttribute('location', 'location');
        element.setAttribute('heading', 'heading');
        element.setAttribute('history', 'history');
        document.body.appendChild(element);

        const scope = $rootScope.$new();
        scope.location = location;
        scope.heading = heading;
        scope.history = history;

        const component = $compile(element)(scope);
        scope.$apply();

        return component;
    }

    describe('The drop up menu', () => {
        it('is hidden by default', () => {
            const component = getComponent();

            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(0);
        });

        it('pops up when the button is clicked', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();
            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(1);
        });

        it('hides when the button is clicked again', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();
            button.click();
            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(0);
        });

        it('can be opened more than once', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();
            button.click();
            button.click();
            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(1);
        });

        it('hides when anywhere else in the page is clicked', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();
            angular.element(document.body).click();
            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(0);
        });

        it('hides when an item is clicked', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            component.find('.qa-straatbeeld-history__item').click();
            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(0);
        });

        it('hides when the external link is clicked', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            component.find('.qa-straatbeeld-history__external-link').click();
            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(0);
        });

        it('does not hide when anywhere else in the menu is clicked', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            component.find('.qa-straatbeeld-history__menu').click();
            component.find('.qa-straatbeeld-history__devider').click();
            component.find('.qa-straatbeeld-history__external').click();

            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(1);
        });
    });

    describe('The external link', () => {
        it('reflects the location and heading', () => {
            const component = getComponent([52.123, 4.789], 12, 12);
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            expect(component.find('.qa-straatbeeld-history__external-link').attr('ng-href'))
                .toEqual('http://maps.google.com/maps?q=&layer=c&cbll=52.123,4.789&cbp=11,12,0,0,0');
        });

        it('updates the location and heading when changed', () => {
            const location = [52.123, 4.789];
            const component = getComponent(location, 12, 12);
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            location[0] = 53.123;
            location[1] = 5.789;
            $rootScope.$digest();

            expect(component.find('.qa-straatbeeld-history__external-link').attr('ng-href'))
                .toEqual('http://maps.google.com/maps?q=&layer=c&cbll=53.123,5.789&cbp=11,12,0,0,0');
        });
    });

    describe('The menu items', () => {
        let clock;

        beforeEach(() => {
            clock = jasmine.clock();
        });

        afterEach(() => {
            clock.uninstall();
        });

        it('The first item is for the most recent street view', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            const items = component.find('.qa-straatbeeld-history__item');

            expect(items.eq(0).text()).toContain('recent');
        });

        it('Does not list any years if the current year is before 2016', () => {
            clock.mockDate(new Date(2015, 3, 9));

            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            const items = component.find('.qa-straatbeeld-history__item');

            expect(items.length).toBe(1);
        });

        it('Lists every year from 2016 till the current year (2016)', () => {
            clock.mockDate(new Date(2016, 11, 31));

            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            const items = component.find('.qa-straatbeeld-history__item');

            expect(items.length).toBe(2);
            expect(items.eq(1).text()).toContain('2016');
        });

        it('Lists every year from 2016 till the current year (2017)', () => {
            clock.mockDate(new Date(2017, 0, 1));

            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            const items = component.find('.qa-straatbeeld-history__item');

            expect(items.length).toBe(3);
            expect(items.eq(1).text()).toContain('2017');
            expect(items.eq(2).text()).toContain('2016');
        });

        it('Lists every year from 2016 till the current year (2020)', () => {
            clock.mockDate(new Date(2020, 10, 16));

            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            const items = component.find('.qa-straatbeeld-history__item');

            expect(items.length).toBe(6);
            expect(items.eq(1).text()).toContain('2020');
            expect(items.eq(2).text()).toContain('2019');
            expect(items.eq(3).text()).toContain('2018');
            expect(items.eq(4).text()).toContain('2017');
            expect(items.eq(5).text()).toContain('2016');
        });

        it('sets the selection', () => {
            let items;

            clock.mockDate(new Date(2020, 10, 16));

            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            expect(button.text()).toContain('recent');

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(1).click();
            expect(button.text()).toContain('2020');

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(3).click();
            expect(button.text()).toContain('2018');

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(5).click();
            expect(button.text()).toContain('2016');

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(0).click();
            expect(button.text()).toContain('recent');
        });

        it('dispatches an action', () => {
            let items;

            clock.mockDate(new Date(2020, 10, 16));

            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(1).click();
            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.SET_STRAATBEELD_HISTORY,
                payload: 2020
            });

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(3).click();
            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.SET_STRAATBEELD_HISTORY,
                payload: 2018
            });

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(5).click();
            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.SET_STRAATBEELD_HISTORY,
                payload: 2016
            });

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(0).click();
            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.SET_STRAATBEELD_HISTORY,
                payload: 0
            });
        });

        it('can have its selection initialized', () => {
            clock.mockDate(new Date(2020, 10, 16));

            const component = getComponent(null, null, 2020);
            const button = component.find('.qa-straatbeeld-history__button');

            expect(button.text()).toContain('2020');

            button.click();
            const items = component.find('.qa-straatbeeld-history__item');

            expect(items.eq(0).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(1).hasClass('c-straatbeeld-history__item--active')).toBeTruthy();
            expect(items.eq(2).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(3).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(4).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(5).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
        });

        it('highlights the selection', () => {
            let items;

            clock.mockDate(new Date(2020, 10, 16));

            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            expect(items.eq(0).hasClass('c-straatbeeld-history__item--active')).toBeTruthy();
            expect(items.eq(1).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(2).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(3).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(4).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(5).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();

            items.eq(2).click();
            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            expect(items.eq(0).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(1).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(2).hasClass('c-straatbeeld-history__item--active')).toBeTruthy();
            expect(items.eq(3).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(4).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(5).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();

            items.eq(4).click();
            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            expect(items.eq(0).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(1).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(2).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(3).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(4).hasClass('c-straatbeeld-history__item--active')).toBeTruthy();
            expect(items.eq(5).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();

            items.eq(0).click();
            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            expect(items.eq(0).hasClass('c-straatbeeld-history__item--active')).toBeTruthy();
            expect(items.eq(1).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(2).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(3).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(4).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
            expect(items.eq(5).hasClass('c-straatbeeld-history__item--active')).toBeFalsy();
        });
    });
});
<commit_msg>Clock based testing replaced with static test<commit_after>describe('The dp-straatbeeld-history component', function () {
    var $compile,
        $rootScope,
        $window,
        store,
        ACTIONS;

    beforeEach(function () {
        angular.mock.module(
            'dpStraatbeeld',
            {
                store: {
                    dispatch: angular.noop
                }
            },
            function ($provide) {
                $provide.value('coordinatesFilter', function (input) {
                    return 'MOCKED_RD_COORDINATES (' + input.join(', ') + ')';
                });
            }
        );

        angular.mock.inject(function (_$compile_, _$rootScope_, _$window_, _store_, _ACTIONS_) {
            $compile = _$compile_;
            $rootScope = _$rootScope_;
            $window = _$window_;
            store = _store_;
            ACTIONS = _ACTIONS_;
        });

        spyOn($window, 'open');
        spyOn(store, 'dispatch');
    });

    function getComponent (location, heading, history) {
        const element = document.createElement('dp-straatbeeld-history');
        element.setAttribute('location', 'location');
        element.setAttribute('heading', 'heading');
        element.setAttribute('history', 'history');
        document.body.appendChild(element);

        const scope = $rootScope.$new();
        scope.location = location;
        scope.heading = heading;
        scope.history = history;

        const component = $compile(element)(scope);
        scope.$apply();

        return component;
    }

    describe('The drop up menu', () => {
        it('is hidden by default', () => {
            const component = getComponent();

            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(0);
        });

        it('pops up when the button is clicked', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();
            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(1);
        });

        it('hides when the button is clicked again', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();
            button.click();
            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(0);
        });

        it('can be opened more than once', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();
            button.click();
            button.click();
            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(1);
        });

        it('hides when anywhere else in the page is clicked', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();
            angular.element(document.body).click();
            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(0);
        });

        it('hides when an item is clicked', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            component.find('.qa-straatbeeld-history__item').click();
            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(0);
        });

        it('hides when the external link is clicked', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            component.find('.qa-straatbeeld-history__external-link').click();
            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(0);
        });

        it('does not hide when anywhere else in the menu is clicked', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            component.find('.qa-straatbeeld-history__menu').click();
            component.find('.qa-straatbeeld-history__devider').click();
            component.find('.qa-straatbeeld-history__external').click();

            expect(component.find('.qa-straatbeeld-history__menu').length).toBe(1);
        });
    });

    describe('The external link', () => {
        it('reflects the location and heading', () => {
            const component = getComponent([52.123, 4.789], 12, 12);
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            expect(component.find('.qa-straatbeeld-history__external-link').attr('ng-href'))
                .toEqual('http://maps.google.com/maps?q=&layer=c&cbll=52.123,4.789&cbp=11,12,0,0,0');
        });

        it('updates the location and heading when changed', () => {
            const location = [52.123, 4.789];
            const component = getComponent(location, 12, 12);
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            location[0] = 53.123;
            location[1] = 5.789;
            $rootScope.$digest();

            expect(component.find('.qa-straatbeeld-history__external-link').attr('ng-href'))
                .toEqual('http://maps.google.com/maps?q=&layer=c&cbll=53.123,5.789&cbp=11,12,0,0,0');
        });
    });

    describe('The menu items', () => {
        it('The first item is for the most recent street view', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            const items = component.find('.qa-straatbeeld-history__item');

            expect(items.eq(0).text()).toContain('recent');
        });

        it('Lists every year from 2016 till the 2017', () => {
            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();

            const items = component.find('.qa-straatbeeld-history__item');

            expect(items.length).toBe(3);
            expect(items.eq(0).text()).toContain('recent');
            expect(items.eq(1).text()).toContain('2017');
            expect(items.eq(2).text()).toContain('2016');
        });

        it('sets the selection', () => {
            let items;

            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            expect(button.text()).toContain('recent');

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(1).click();
            expect(button.text()).toContain('2017');

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(2).click();
            expect(button.text()).toContain('2016');

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(0).click();
            expect(button.text()).toContain('recent');
        });

        it('dispatches an action', () => {
            let items;

            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(1).click();
            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.SET_STRAATBEELD_HISTORY,
                payload: 2017
            });

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(2).click();
            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.SET_STRAATBEELD_HISTORY,
                payload: 2016
            });

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            items.eq(0).click();
            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.SET_STRAATBEELD_HISTORY,
                payload: 0
            });
        });

        it('can have its selection initialized', () => {
            const component = getComponent(null, null, 2017);
            const button = component.find('.qa-straatbeeld-history__button');

            expect(button.text()).toContain('2017');

            button.click();
            const items = component.find('.qa-straatbeeld-history__item');

            expect(items.eq(0).hasClass('c-straatbeeld-history__item--active')).toBe(false);
            expect(items.eq(1).hasClass('c-straatbeeld-history__item--active')).toBe(true);
            expect(items.eq(2).hasClass('c-straatbeeld-history__item--active')).toBe(false);
            expect(items.eq(3).hasClass('c-straatbeeld-history__item--active')).toBe(false);
        });

        it('highlights the selection', () => {
            let items;

            const component = getComponent();
            const button = component.find('.qa-straatbeeld-history__button');

            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            expect(items.eq(0).hasClass('c-straatbeeld-history__item--active')).toBe(true);
            expect(items.eq(1).hasClass('c-straatbeeld-history__item--active')).toBe(false);
            expect(items.eq(2).hasClass('c-straatbeeld-history__item--active')).toBe(false);
            expect(items.eq(3).hasClass('c-straatbeeld-history__item--active')).toBe(false);

            items.eq(2).click();
            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            expect(items.eq(0).hasClass('c-straatbeeld-history__item--active')).toBe(false);
            expect(items.eq(1).hasClass('c-straatbeeld-history__item--active')).toBe(false);
            expect(items.eq(2).hasClass('c-straatbeeld-history__item--active')).toBe(true);
            expect(items.eq(3).hasClass('c-straatbeeld-history__item--active')).toBe(false);

            items.eq(0).click();
            button.click();
            items = component.find('.qa-straatbeeld-history__item');
            expect(items.eq(0).hasClass('c-straatbeeld-history__item--active')).toBe(true);
            expect(items.eq(1).hasClass('c-straatbeeld-history__item--active')).toBe(false);
            expect(items.eq(2).hasClass('c-straatbeeld-history__item--active')).toBe(false);
            expect(items.eq(3).hasClass('c-straatbeeld-history__item--active')).toBe(false);
        });
    });
});
<|endoftext|>


<commit_before>define(['dataHelper'], function (helper) {
    "use strict";

	var urls = {
		conferenceUrl: null,
		bookingsUrl: null,
		adminUrl: null,
		keyCloakUrl: null,
		errorState: false
	};

    function isInitialized() {
        return (urls.conferenceUrl !== null && urls.bookingsUrl !== null && urls.adminUrl !== null && urls.keyCloakUrl !== null) || urls.errorState;
    }

    function httpRequest(url, method, onSuccess, onError, headers) {
        if (urls.errorState) {
            if (onError) {
                onError({status: 0, statusMessage: "error during initializiation"});
            }
            return;
        }

        console.log(method + " " + url);
        var xhttp = new XMLHttpRequest();
        xhttp.open(method, url, true);

        if (headers) {
            var key, headerKeys = Object.keys(headers);
            for (key in headerKeys) {
                xhttp.setRequestHeader(headerKeys[key], headers[headerKeys[key]]);
            }
        }

        xhttp.onreadystatechange = function () {
            if (this.readyState === 4) {
                if (this.status >= 200 && this.status <= 205 && onSuccess) {
                    onSuccess(this.response ? JSON.parse(this.response) : null);
                } else if (onError) {
                    onError(this);
                }
            }
        };
        xhttp.send();
    }

    function initialize(callback) {
        httpRequest(
            "init.json",
            "GET",
            function (result) {
                urls.keyCloakUrl = result.keycloak;
				urls.conferenceUrl = result.conferences;
				urls.bookingsUrl = result.events;
				urls.adminUrl = result.admin || "";
				urls.adminUrl = urls.adminUrl.replace(/\/$/ig, ""); // make sure there is no space at the end
				urls.errorState = false;
                if (callback) {
                    callback(urls);
                }
                console.log("Requests initialized");
            },
            function () {
                console.log("Error during initialization!");
				urls.errorState = true;
            }
        );
    }

    function getDelta(conference, onSuccess, onError) {
        httpRequest(
			urls.bookingsUrl,
            "GET",
            function (result) {
                result.events = helper.addDeltaToConferences(conference.events, result);
                onSuccess(conference);
            },
            onError
        );
    }

    function getConferences(onSuccess, onError) {
        function doRequest() {
            httpRequest(
				urls.conferenceUrl,
                "GET",
                onSuccess,
                onError
            );
        }

        if (!isInitialized()) {
            initialize(doRequest);
        } else {
            doRequest();
        }

    }

    function getWithToken(url, keycloakToken, onSuccess, onError) {
        var headers = {};
        function doRequest() {
            if (keycloakToken) {
                headers.Authorization = "Bearer " + keycloakToken;
            }
            httpRequest(
				url,
                "GET",
                function(data) {
				    console.log(data);
				    onSuccess(data);
                },
                onError,
                headers
            );
        }

        if (!isInitialized()) {
            initialize(doRequest);
        } else {
            doRequest();
        }

    }

    function getList(onSuccess, onError) {
        getConferences(
            function (result) {
                result.events = helper.filterAndSortEvents(result.events);
                getDelta(result, onSuccess, onError)
            },
            onError
        );
    }

    function update(talk, onSuccess, onError, keycloakToken) {
        if (!isInitialized()) {
            initialize();
        }

        var headers = {};
        if (keycloakToken) {
            headers.Authorization = "Bearer " + keycloakToken;
        }
        httpRequest(urls.adminUrl + "/" + talk.id, talk.fullyBooked ? "POST" : "DELETE", onSuccess, onError, headers);
    }

    return {
		initialize : initialize,
        get: getList,
        update: update,
        getWithToken: getWithToken
    };
});<commit_msg>#3 ui-changes, no function yet - revert accidental push<commit_after>define(['dataHelper'], function (helper) {
    "use strict";

	var urls = {
		conferenceUrl: null,
		bookingsUrl: null,
		adminUrl: null,
		keyCloakUrl: null,
		errorState: false
	};

    function isInitialized() {
        return (urls.conferenceUrl !== null && urls.bookingsUrl !== null && urls.adminUrl !== null && urls.keyCloakUrl !== null) || urls.errorState;
    }

    function httpRequest(url, method, onSuccess, onError, headers) {
        if (urls.errorState) {
            if (onError) {
                onError({status: 0, statusMessage: "error during initializiation"});
            }
            return;
        }

        console.log(method + " " + url);
        var xhttp = new XMLHttpRequest();
        xhttp.open(method, url, true);

        if (headers) {
            var key, headerKeys = Object.keys(headers);
            for (key in headerKeys) {
                xhttp.setRequestHeader(headerKeys[key], headers[headerKeys[key]]);
            }
        }

        xhttp.onreadystatechange = function () {
            if (this.readyState === 4) {
                if (this.status >= 200 && this.status <= 205 && onSuccess) {
                    onSuccess(this.response ? JSON.parse(this.response) : null);
                } else if (onError) {
                    onError(this);
                }
            }
        };
        xhttp.send();
    }

    function initialize(callback) {
        httpRequest(
            "../rest/init.json",
            "GET",
            function (result) {
                urls.keyCloakUrl = result.keycloak;
				urls.conferenceUrl = result.conferences;
				urls.bookingsUrl = result.events;
				urls.adminUrl = result.admin || "";
				urls.adminUrl = urls.adminUrl.replace(/\/$/ig, ""); // make sure there is no space at the end
				urls.errorState = false;
                if (callback) {
                    callback(urls);
                }
                console.log("Requests initialized");
            },
            function () {
                console.log("Error during initialization!");
				urls.errorState = true;
            }
        );
    }

    function getDelta(conference, onSuccess, onError) {
        httpRequest(
			urls.bookingsUrl,
            "GET",
            function (result) {
                result.events = helper.addDeltaToConferences(conference.events, result);
                onSuccess(conference);
            },
            onError
        );
    }

    function getConferences(onSuccess, onError) {
        function doRequest() {
            httpRequest(
				urls.conferenceUrl,
                "GET",
                onSuccess,
                onError
            );
        }

        if (!isInitialized()) {
            initialize(doRequest);
        } else {
            doRequest();
        }

    }

    function getWithToken(url, keycloakToken, onSuccess, onError) {
        var headers = {};
        function doRequest() {
            if (keycloakToken) {
                headers.Authorization = "Bearer " + keycloakToken;
            }
            httpRequest(
				url,
                "GET",
                function(data) {
				    console.log(data);
				    onSuccess(data);
                },
                onError,
                headers
            );
        }

        if (!isInitialized()) {
            initialize(doRequest);
        } else {
            doRequest();
        }

    }

    function getList(onSuccess, onError) {
        getConferences(
            function (result) {
                result.events = helper.filterAndSortEvents(result.events);
                getDelta(result, onSuccess, onError)
            },
            onError
        );
    }

    function update(talk, onSuccess, onError, keycloakToken) {
        if (!isInitialized()) {
            initialize();
        }

        var headers = {};
        if (keycloakToken) {
            headers.Authorization = "Bearer " + keycloakToken;
        }
        httpRequest(urls.adminUrl + "/" + talk.id, talk.fullyBooked ? "POST" : "DELETE", onSuccess, onError, headers);
    }

    return {
		initialize : initialize,
        get: getList,
        update: update,
        getWithToken: getWithToken
    };
});<|endoftext|>