<commit_before>define(['../system', '../viewModel', '../app'], function (system, viewModel, app) {

    //NOTE: Sammy.js is not required by the core of Durandal.
    //However, this plugin leverages it to enable navigation.

    var routesByPath = {},
        allRoutes = ko.observableArray([]),
        visibleRoutes = ko.observableArray([]),
        ready = ko.observable(false),
        isNavigating = ko.observable(false),
        sammy,
        router,
        previousRoute,
        previousModule,
        cancelling = false,
        activeItem = viewModel.activator(),
        activeRoute = ko.observable(),
        navigationDefaultRoute,
        queue = [],
        skipRouteUrl;

    var tryActivateRouter = function () {
        tryActivateRouter = system.noop;
        ready(true);
        router.dfd.resolve();
        delete router.dfd;
    };

    activeItem.settings.areSameItem = function (currentItem, newItem, activationData) {
        return false;
    };

    function redirect(url) {
        isNavigating(false);
        system.log('Redirecting');
        router.navigateTo(url);
    }

    function cancelNavigation() {
        cancelling = true;
        system.log('Cancelling Navigation');

        if (previousRoute) {
            sammy.setLocation(previousRoute);
        }

        cancelling = false;
        isNavigating(false);

        var routeAttempted = sammy.last_location[1].split('#/')[1];

        if (previousRoute || !routeAttempted) {
            tryActivateRouter();
        } else if (routeAttempted != navigationDefaultRoute) {
            window.location.replace("#/" + navigationDefaultRoute);
        } else {
            tryActivateRouter();
        }
    }

    function completeNavigation(routeInfo, params, module) {
        activeRoute(routeInfo);
        router.onNavigationComplete(routeInfo, params, module);
        previousModule = module;
        previousRoute = sammy.last_location[1].replace('/', '');
        tryActivateRouter();
    }

    function activateRoute(routeInfo, params, module) {
        system.log('Activating Route', routeInfo, module, params);

        activeItem.activateItem(module, params).then(function (succeeded) {
            if (succeeded) {
                completeNavigation(routeInfo, params, module);
            } else {
                cancelNavigation();
            }
        });
    }

    function shouldStopNavigation() {
        return cancelling || (sammy.last_location[1].replace('/', '') == previousRoute);
    }

    function handleGuardedRoute(routeInfo, params, instance) {
        var resultOrPromise = router.guardRoute(routeInfo, params, instance);
        if (resultOrPromise) {
            if (resultOrPromise.then) {
                resultOrPromise.then(function(result) {
                    if (result) {
                        if (typeof result == 'string') {
                            redirect(result);
                        } else {
                            activateRoute(routeInfo, params, instance);
                        }
                    } else {
                        cancelNavigation();
                    }
                });
            } else {
                if (typeof resultOrPromise == 'string') {
                    redirect(resultOrPromise);
                } else {
                    activateRoute(routeInfo, params, instance);
                }
            }
        } else {
            cancelNavigation();
        }
    }

    function dequeueRoute() {
        if (isNavigating()) {
            return;
        }

        var next = queue.shift();
        queue = [];

        if (!next) {
            return;
        }

        isNavigating(true);

        system.acquire(next.routeInfo.moduleId).then(function(module) {
            next.params.routeInfo = next.routeInfo;
            next.params.router = router;

            var instance = router.getActivatableInstance(next.routeInfo, next.params, module);

            if (router.guardRoute) {
                handleGuardedRoute(next.routeInfo, next.params, instance);
            } else {
                activateRoute(next.routeInfo, next.params, instance);
            }
        });
    }

    function queueRoute(routeInfo, params) {
        queue.unshift({
            routeInfo: routeInfo,
            params: params
        });

        dequeueRoute();
    }

    function ensureRoute(route, params) {
        var routeInfo = routesByPath[route];

        if (shouldStopNavigation()) {
            return;
        }

        if (!routeInfo) {
            if (!router.autoConvertRouteToModuleId) {
                router.handleInvalidRoute(route, params);
                return;
            }

            var routeName = router.convertRouteToName(route);
            routeInfo = {
                moduleId: router.autoConvertRouteToModuleId(route, params),
                name: routeName,
                caption: routeName
            };
        }

        queueRoute(routeInfo, params);
    }

    function handleDefaultRoute() {
        ensureRoute(navigationDefaultRoute, this.params || {});
    }

    function handleMappedRoute() {
        ensureRoute(this.app.last_route.path.toString(), this.params || {});
    }

    function handleWildCardRoute() {
        var params = this.params || {}, route;

        if (router.autoConvertRouteToModuleId) {
            var fragment = this.path.split('#/');

            if (fragment.length == 2) {
                var parts = fragment[1].split('/');
                route = parts[0];
                params.splat = parts.splice(1);
                ensureRoute(route, params);
                return;
            }
        }

        router.handleInvalidRoute(this.app.last_location[1], params);
    }

    function configureRoute(routeInfo) {
        router.prepareRouteInfo(routeInfo);

        routesByPath[routeInfo.url.toString()] = routeInfo;
        allRoutes.push(routeInfo);

        if (routeInfo.visible) {
            routeInfo.isActive = ko.computed(function () {
                return ready() && activeItem() && activeItem().__moduleId__ == routeInfo.moduleId;
            });

            visibleRoutes.push(routeInfo);
        }

        return routeInfo;
    }

    return router = {
        ready: ready,
        allRoutes: allRoutes,
        visibleRoutes: visibleRoutes,
        isNavigating: isNavigating,
        activeItem: activeItem,
        activeRoute: activeRoute,
        afterCompose: function () {
            setTimeout(function () {
                isNavigating(false);
                dequeueRoute();
            }, 10);
        },
        getActivatableInstance: function (routeInfo, params, module) {
            if (typeof module == 'function') {
                return new module();
            } else {
                return module;
            }
        },
        useConvention: function (rootPath) {
            rootPath = rootPath == null ? 'viewmodels' : rootPath;
            if (rootPath) {
                rootPath += '/';
            }
            router.convertRouteToModuleId = function (url) {
                return rootPath + router.stripParameter(url);
            };
        },
        stripParameter: function (val) {
            var colonIndex = val.indexOf(':');
            var length = colonIndex > 0 ? colonIndex - 1 : val.length;
            return val.substring(0, length);
        },
        handleInvalidRoute: function (route, params) {
            system.log('No Route Found', route, params);
        },
        onNavigationComplete: function (routeInfo, params, module) {
            if (app.title) {
                document.title = routeInfo.caption + " | " + app.title;
            } else {
                document.title = routeInfo.caption;
            }
        },
        navigateBack: function () {
            window.history.back();
        },
        navigateTo: function (url, option) {
            option = option || 'trigger';

            switch (option.toLowerCase()) {
                case 'skip':
                    skipRouteUrl = url;
                    sammy.setLocation(url);
                    break;
                case 'replace':
                    window.location.replace(url);
                    break;
                default:
                    if (sammy.lookupRoute('get', url)) {
                        sammy.setLocation(url);
                    } else {
                        window.location.href = url;
                    }
                    break;
            }
        },
        replaceLocation: function (url) {
            this.navigateTo(url, 'replace');
        },
        convertRouteToName: function (route) {
            var value = router.stripParameter(route);
            return value.substring(0, 1).toUpperCase() + value.substring(1);
        },
        convertRouteToModuleId: function (route) {
            return router.stripParameter(route);
        },
        prepareRouteInfo: function (info) {
            if (!(info.url instanceof RegExp)) {
                info.name = info.name || router.convertRouteToName(info.url);
                info.moduleId = info.moduleId || router.convertRouteToModuleId(info.url);
                info.hash = info.hash || '#/' + info.url;
            }

            info.caption = info.caption || info.name;
            info.settings = info.settings || {};
        },
        mapAuto: function (path) {
            path = path || 'viewmodels';
            path += '/';

            router.autoConvertRouteToModuleId = function (url, params) {
                return path + router.stripParameter(url);
            };
        },
        mapNav: function (urlOrConfig, moduleId, name) {
            if (typeof urlOrConfig == "string") {
                return this.mapRoute(urlOrConfig, moduleId, name, true);
            }

            urlOrConfig.visible = true;
            return configureRoute(urlOrConfig);
        },
        mapRoute: function (urlOrConfig, moduleId, name, visible) {
            if (typeof urlOrConfig == "string") {
                return configureRoute({
                    url: urlOrConfig,
                    moduleId: moduleId,
                    name: name,
                    visible: visible
                });
            } else {
                return configureRoute(urlOrConfig);
            }
        },
        map: function (routeOrRouteArray) {
            if (!system.isArray(routeOrRouteArray)) {
                return configureRoute(routeOrRouteArray);
            }

            var configured = [];
            for (var i = 0; i < routeOrRouteArray.length; i++) {
                configured.push(configureRoute(routeOrRouteArray[i]));
            }
            return configured;
        },
        activate: function (defaultRoute) {
            return system.defer(function (dfd) {
                var processedRoute;

                router.dfd = dfd;
                navigationDefaultRoute = defaultRoute;

                sammy = Sammy(function (route) {
                    var unwrapped = allRoutes();

                    for (var i = 0; i < unwrapped.length; i++) {
                        var current = unwrapped[i];
                        route.get(current.url, handleMappedRoute);
                        processedRoute = this.routes.get[i];
                        routesByPath[processedRoute.path.toString()] = current;
                    }

                    route.get('#/', handleDefaultRoute);
                    route.get(/\#\/(.*)/, handleWildCardRoute);
                });

                sammy._checkFormSubmission = function () {
                    return false;
                };

                sammy.before(null, function(context) {
                    if (!skipRouteUrl) {
                        return true;
                    } else if (context.path === "/" + skipRouteUrl) {
                        skipRouteUrl = null;
                        return false;
                    } else {
                        throw new Error("Expected to skip url '" + skipRouteUrl + "', but found url '" + context.path + "'");
                    }
                });

                sammy.log = function () {
                    var args = Array.prototype.slice.call(arguments, 0);
                    args.unshift('Sammy');
                    system.log.apply(system, args);
                };

                sammy.run('#/');
            }).promise();
        }
    };
});
<commit_msg>Added onRouteComposed hook to router.<commit_after>define(['../system', '../viewModel', '../app'], function (system, viewModel, app) {

    //NOTE: Sammy.js is not required by the core of Durandal.
    //However, this plugin leverages it to enable navigation.

    var routesByPath = {},
        allRoutes = ko.observableArray([]),
        visibleRoutes = ko.observableArray([]),
        ready = ko.observable(false),
        isNavigating = ko.observable(false),
        sammy,
        router,
        previousRoute,
        previousModule,
        cancelling = false,
        activeItem = viewModel.activator(),
        activeRoute = ko.observable(),
        navigationDefaultRoute,
        queue = [],
        skipRouteUrl;

    var tryActivateRouter = function () {
        tryActivateRouter = system.noop;
        ready(true);
        router.dfd.resolve();
        delete router.dfd;
    };

    activeItem.settings.areSameItem = function (currentItem, newItem, activationData) {
        return false;
    };

    function redirect(url) {
        isNavigating(false);
        system.log('Redirecting');
        router.navigateTo(url);
    }

    function cancelNavigation() {
        cancelling = true;
        system.log('Cancelling Navigation');

        if (previousRoute) {
            sammy.setLocation(previousRoute);
        }

        cancelling = false;
        isNavigating(false);

        var routeAttempted = sammy.last_location[1].split('#/')[1];

        if (previousRoute || !routeAttempted) {
            tryActivateRouter();
        } else if (routeAttempted != navigationDefaultRoute) {
            window.location.replace("#/" + navigationDefaultRoute);
        } else {
            tryActivateRouter();
        }
    }

    function completeNavigation(routeInfo, params, module) {
        activeRoute(routeInfo);
        router.onNavigationComplete(routeInfo, params, module);
        previousModule = module;
        previousRoute = sammy.last_location[1].replace('/', '');
        tryActivateRouter();
    }

    function activateRoute(routeInfo, params, module) {
        system.log('Activating Route', routeInfo, module, params);

        activeItem.activateItem(module, params).then(function (succeeded) {
            if (succeeded) {
                completeNavigation(routeInfo, params, module);
            } else {
                cancelNavigation();
            }
        });
    }

    function shouldStopNavigation() {
        return cancelling || (sammy.last_location[1].replace('/', '') == previousRoute);
    }

    function handleGuardedRoute(routeInfo, params, instance) {
        var resultOrPromise = router.guardRoute(routeInfo, params, instance);
        if (resultOrPromise) {
            if (resultOrPromise.then) {
                resultOrPromise.then(function(result) {
                    if (result) {
                        if (typeof result == 'string') {
                            redirect(result);
                        } else {
                            activateRoute(routeInfo, params, instance);
                        }
                    } else {
                        cancelNavigation();
                    }
                });
            } else {
                if (typeof resultOrPromise == 'string') {
                    redirect(resultOrPromise);
                } else {
                    activateRoute(routeInfo, params, instance);
                }
            }
        } else {
            cancelNavigation();
        }
    }

    function dequeueRoute() {
        if (isNavigating()) {
            return;
        }

        var next = queue.shift();
        queue = [];

        if (!next) {
            return;
        }

        isNavigating(true);

        system.acquire(next.routeInfo.moduleId).then(function(module) {
            next.params.routeInfo = next.routeInfo;
            next.params.router = router;

            var instance = router.getActivatableInstance(next.routeInfo, next.params, module);

            if (router.guardRoute) {
                handleGuardedRoute(next.routeInfo, next.params, instance);
            } else {
                activateRoute(next.routeInfo, next.params, instance);
            }
        });
    }

    function queueRoute(routeInfo, params) {
        queue.unshift({
            routeInfo: routeInfo,
            params: params
        });

        dequeueRoute();
    }

    function ensureRoute(route, params) {
        var routeInfo = routesByPath[route];

        if (shouldStopNavigation()) {
            return;
        }

        if (!routeInfo) {
            if (!router.autoConvertRouteToModuleId) {
                router.handleInvalidRoute(route, params);
                return;
            }

            var routeName = router.convertRouteToName(route);
            routeInfo = {
                moduleId: router.autoConvertRouteToModuleId(route, params),
                name: routeName,
                caption: routeName
            };
        }

        queueRoute(routeInfo, params);
    }

    function handleDefaultRoute() {
        ensureRoute(navigationDefaultRoute, this.params || {});
    }

    function handleMappedRoute() {
        ensureRoute(this.app.last_route.path.toString(), this.params || {});
    }

    function handleWildCardRoute() {
        var params = this.params || {}, route;

        if (router.autoConvertRouteToModuleId) {
            var fragment = this.path.split('#/');

            if (fragment.length == 2) {
                var parts = fragment[1].split('/');
                route = parts[0];
                params.splat = parts.splice(1);
                ensureRoute(route, params);
                return;
            }
        }

        router.handleInvalidRoute(this.app.last_location[1], params);
    }

    function configureRoute(routeInfo) {
        router.prepareRouteInfo(routeInfo);

        routesByPath[routeInfo.url.toString()] = routeInfo;
        allRoutes.push(routeInfo);

        if (routeInfo.visible) {
            routeInfo.isActive = ko.computed(function () {
                return ready() && activeItem() && activeItem().__moduleId__ == routeInfo.moduleId;
            });

            visibleRoutes.push(routeInfo);
        }

        return routeInfo;
    }

    return router = {
        ready: ready,
        allRoutes: allRoutes,
        visibleRoutes: visibleRoutes,
        isNavigating: isNavigating,
        activeItem: activeItem,
        activeRoute: activeRoute,
        afterCompose: function () {
            setTimeout(function () {
                isNavigating(false);
                dequeueRoute();
                router.onRouteComposed && router.onRouteComposed(router.activeRoute());
            }, 10);
        },
        getActivatableInstance: function (routeInfo, params, module) {
            if (typeof module == 'function') {
                return new module();
            } else {
                return module;
            }
        },
        useConvention: function (rootPath) {
            rootPath = rootPath == null ? 'viewmodels' : rootPath;
            if (rootPath) {
                rootPath += '/';
            }
            router.convertRouteToModuleId = function (url) {
                return rootPath + router.stripParameter(url);
            };
        },
        stripParameter: function (val) {
            var colonIndex = val.indexOf(':');
            var length = colonIndex > 0 ? colonIndex - 1 : val.length;
            return val.substring(0, length);
        },
        handleInvalidRoute: function (route, params) {
            system.log('No Route Found', route, params);
        },
        onNavigationComplete: function (routeInfo, params, module) {
            if (app.title) {
                document.title = routeInfo.caption + " | " + app.title;
            } else {
                document.title = routeInfo.caption;
            }
        },
        navigateBack: function () {
            window.history.back();
        },
        navigateTo: function (url, option) {
            option = option || 'trigger';

            switch (option.toLowerCase()) {
                case 'skip':
                    skipRouteUrl = url;
                    sammy.setLocation(url);
                    break;
                case 'replace':
                    window.location.replace(url);
                    break;
                default:
                    if (sammy.lookupRoute('get', url)) {
                        sammy.setLocation(url);
                    } else {
                        window.location.href = url;
                    }
                    break;
            }
        },
        replaceLocation: function (url) {
            this.navigateTo(url, 'replace');
        },
        convertRouteToName: function (route) {
            var value = router.stripParameter(route);
            return value.substring(0, 1).toUpperCase() + value.substring(1);
        },
        convertRouteToModuleId: function (route) {
            return router.stripParameter(route);
        },
        prepareRouteInfo: function (info) {
            if (!(info.url instanceof RegExp)) {
                info.name = info.name || router.convertRouteToName(info.url);
                info.moduleId = info.moduleId || router.convertRouteToModuleId(info.url);
                info.hash = info.hash || '#/' + info.url;
            }

            info.caption = info.caption || info.name;
            info.settings = info.settings || {};
        },
        mapAuto: function (path) {
            path = path || 'viewmodels';
            path += '/';

            router.autoConvertRouteToModuleId = function (url, params) {
                return path + router.stripParameter(url);
            };
        },
        mapNav: function (urlOrConfig, moduleId, name) {
            if (typeof urlOrConfig == "string") {
                return this.mapRoute(urlOrConfig, moduleId, name, true);
            }

            urlOrConfig.visible = true;
            return configureRoute(urlOrConfig);
        },
        mapRoute: function (urlOrConfig, moduleId, name, visible) {
            if (typeof urlOrConfig == "string") {
                return configureRoute({
                    url: urlOrConfig,
                    moduleId: moduleId,
                    name: name,
                    visible: visible
                });
            } else {
                return configureRoute(urlOrConfig);
            }
        },
        map: function (routeOrRouteArray) {
            if (!system.isArray(routeOrRouteArray)) {
                return configureRoute(routeOrRouteArray);
            }

            var configured = [];
            for (var i = 0; i < routeOrRouteArray.length; i++) {
                configured.push(configureRoute(routeOrRouteArray[i]));
            }
            return configured;
        },
        activate: function (defaultRoute) {
            return system.defer(function (dfd) {
                var processedRoute;

                router.dfd = dfd;
                navigationDefaultRoute = defaultRoute;

                sammy = Sammy(function (route) {
                    var unwrapped = allRoutes();

                    for (var i = 0; i < unwrapped.length; i++) {
                        var current = unwrapped[i];
                        route.get(current.url, handleMappedRoute);
                        processedRoute = this.routes.get[i];
                        routesByPath[processedRoute.path.toString()] = current;
                    }

                    route.get('#/', handleDefaultRoute);
                    route.get(/\#\/(.*)/, handleWildCardRoute);
                });

                sammy._checkFormSubmission = function () {
                    return false;
                };

                sammy.before(null, function(context) {
                    if (!skipRouteUrl) {
                        return true;
                    } else if (context.path === "/" + skipRouteUrl) {
                        skipRouteUrl = null;
                        return false;
                    } else {
                        throw new Error("Expected to skip url '" + skipRouteUrl + "', but found url '" + context.path + "'");
                    }
                });

                sammy.log = function () {
                    var args = Array.prototype.slice.call(arguments, 0);
                    args.unshift('Sammy');
                    system.log.apply(system, args);
                };

                sammy.run('#/');
            }).promise();
        }
    };
});
<|endoftext|>


<commit_before>/*
* Moon 0.1.3
* Copyright 2016, Kabir Shah
* https://github.com/KingPixil/moon/
* Free to use under the MIT license.
* https://kingpixil.github.io/license
*/

"use strict";
(function(window) {

    /* ======= Global Variables ======= */
    var config = {
      silent: false
    }
    var directives = {};
    var components = {};

    /* ======= Global Utilities ======= */

    /**
    * Converts attributes into key-value pairs
    * @param {Node} node
    * @return {Object} Key-Value pairs of Attributes
    */
    var extractAttrs = function(node) {
      var attrs = {};
      if(!node.attributes) return attrs;
      var rawAttrs = node.attributes;
      for(var i = 0; i < rawAttrs.length; i++) {
        attrs[rawAttrs[i].name] = rawAttrs[i].value
      }

      return attrs;
    }

    /**
    * Compiles a template with given data
    * @param {String} template
    * @param {Object} data
    * @return {String} Template with data rendered
    */
    var compileTemplate = function(template, data) {
      var code = template,
          re = /{{([A-Za-z0-9_.()\[\]]+)}}/gi;
      code.replace(re, function(match, p) {
        code = code.replace(match, "` + data." + p + " + `");
      });
      var compile = new Function("data", "var out = `" + code + "`; return out");
      var output = compile(data);
      return output;
    }

    /**
    * Gets Root Element
    * @param {String} html
    * @return {Node} Root Element
    */
    var getRootElement = function(html) {
      var dummy = document.createElement('div');
      dummy.innerHTML = html;
      return dummy.firstChild;
    }

    /**
    * Merges two Objects
    * @param {Object} obj
    * @param {Object} obj2
    * @return {Object} Merged Objects
    */
    function merge(obj, obj2) {
      for (var key in obj2) {
        if (obj2.hasOwnProperty(key)) obj[key] = obj2[key];
      }
      return obj;
    }

    /**
    * Creates an object to be used in a Virtual DOM
    * @param {String} type
    * @param {Array} children
    * @param {String} val
    * @param {Object} props
    * @param {Node} node
    * @return {Object} Object usable in Virtual DOM
    */
    var createElement = function(type, children, val, props, node) {
      return {type: type, children: children, val: val, props: props, node: node};
    }

    /**
    * Create Elements Recursively For all Children
    * @param {Array} children
    * @return {Array} Array of elements usable in Virtual DOM
    */
    var recursiveChildren = function(children) {
      var recursiveChildrenArr = [];
      for(var i = 0; i < children.length; i++) {
        var child = children[i];
        recursiveChildrenArr.push(createElement(child.nodeName, recursiveChildren(child.childNodes), child.textContent, extractAttrs(child), child));
      }
      return recursiveChildrenArr;
    }

    /**
    * Creates Virtual DOM
    * @param {Node} node
    * @return {Object} Virtual DOM
    */
    var createVirtualDOM = function(node) {
      var vdom = createElement(node.nodeName, recursiveChildren(node.childNodes), node.textContent, extractAttrs(node), node);
      return vdom;
    }

    /**
     * Compiles JSX to HTML
     * @param {String} tag
     * @param {Object} attrs
     * @param {Array} children
     * @return {String} HTML compiled from JSX
     */
    var h = function(tag, attrs, children) {
      var args = Array.prototype.slice.call(arguments);
      var tag = args.shift();
      var attrs = args.shift();
      var children = args.join("");
      return "<" + tag + Object.keys(attrs||{}).reduce(function(total, current) {return (total||" ") + current + "='" + attrs[current] + "'"}, "") + ">" + children + "<" + tag + "/>";
    };

    /**
     * Sets the Elements Initial Value
     * @param {Node} el
     * @param {String} value
     */
    var setInitialElementValue = function(el, value) {
      el.innerHTML = value;
    }

    /**
     * Does No Operation
     */
    var noop = function() {

    }

    function Moon(opts) {
        /* ======= Initial Values ======= */
        var _el = opts.el;
        var _data = opts.data;
        var self = this;
        this.$el = document.querySelector(_el);
        this.$template = opts.template || this.$el.innerHTML;
        this.$hooks = merge({created: noop, mounted: noop, updated: noop, destroyed: noop}, opts.hooks);
        this.$methods = opts.methods || {};
        this.$components = merge(opts.components || {}, components);
        this.$dom = {type: this.$el.nodeName, children: [], node: this.$el};
        this.$destroyed = false;

        /* ======= Listen for Changes ======= */
        Object.defineProperty(this, '$data', {
            get: function() {
                return _data;
            },
            set: function(value) {
                _data = value;
                this.build(this.$dom.children);
            },
            configurable: true
        });

        /* ======= Default Directives ======= */
        directives["m-if"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.textContent = "";
          } else {
            el.textContent = compileTemplate(vdom.val, self.$data);
          }
        }

        directives["m-show"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.style.display = 'none';
          } else {
            el.style.display = 'block';
          }
        }

        directives["m-on"] = function(el, val, vdom) {
          var splitVal = val.split(":");
          var eventToCall = splitVal[0];
          var methodToCall = splitVal[1];
          el.addEventListener(eventToCall, function() {
            self.method(methodToCall);
          });
          delete vdom.props["m-on"];
        }

        directives["m-model"] = function(el, val, vdom) {
          el.value = self.get(val);
          el.addEventListener("input", function() {
            self.set(val, el.value);
          });
          delete vdom.props["m-model"];
        }

        directives["m-for"] = function(el, val, vdom) {
          var parts = val.split(" in ");
          var alias = parts[0];
          var array = self.get(parts[1]);
        }

        directives["m-once"] = function(el, val, vdom) {
          vdom.val = el.textContent;
          for(var child in vdom.children) {
            vdom.children[child].val = compileTemplate(vdom.children[child].val, self.$data);
          }
        }

        directives["m-text"] = function(el, val, vdom) {
          el.textContent = val;
        }

        directives["m-html"] = function(el, val, vdom) {
          el.innerHTML = val;
        }

        directives["m-mask"] = function(el, val, vdom) {

        }

        /* ======= Initialize 🎉 ======= */
        this.init();
    }

    /* ======= Instance Methods ======= */

    var hasConsole = typeof window.console !== undefined;

    /**
    * Logs a Message
    * @param {String} msg
    */
    Moon.prototype.log = function(msg) {
      if(!config.silent && hasConsole) console.log(msg);
    }

    /**
    * Throws an Error
    * @param {String} msg
    */
    Moon.prototype.error = function(msg) {
      if(hasConsole) console.error("[Moon] ERR: " + msg);
    }

    /**
    * Sets Value in Data
    * @param {String} key
    * @param {String} val
    */
    Moon.prototype.set = function(key, val) {
      this.$data[key] = val;
      if(!this.$destroyed) this.build(this.$dom.children);
      this.$hooks.updated();
    }

    /**
    * Gets Value in Data
    * @param {String} key
    * @return {String} Value of key in data
    */
    Moon.prototype.get = function(key) {
      return this.$data[key];
    }

    /**
    * Calls a method
    * @param {String} method
    */
    Moon.prototype.method = function(method) {
      this.$methods[method]();
    }

    /**
    * Destroys Moon Instance
    */
    Moon.prototype.destroy = function() {
      Object.defineProperty(this, '$data', {
        set: function(value) {
          _data = value;
        }
      });
      this.$destroyed = true;
      this.$hooks.destroyed();
    }

    /**
    * Builds the DOM With Data
    * @param {Array} children
    */
    Moon.prototype.build = function(children) {
      for(var i = 0; i < children.length; i++) {
        var el = children[i];

        if(el.type === "#text") {
          el.node.textContent = compileTemplate(el.val, this.$data);
        } else if(el.props) {
          for(var prop in el.props) {
            var propVal = el.props[prop];
            var compiledProperty = compileTemplate(propVal, this.$data);
            var directive = directives[prop];
            if(directive) {
              el.node.removeAttribute(prop);
              directive(el.node, compiledProperty, el);
            }

            if(!directive) el.node.setAttribute(prop, compiledProperty);
          }
        }

        this.build(el.children);
      }
    }

    /**
    * Initializes Moon
    */
    Moon.prototype.init = function() {
      this.log("======= Moon =======");
      this.$hooks.created();
      setInitialElementValue(this.$el, this.$template);
      this.$dom = createVirtualDOM(this.$el);
      this.build(this.$dom.children);
      this.$hooks.mounted();
    }

    /* ======= Global API ======= */

    /**
    * Sets the Configuration of Moon
    * @param {Object} opts
    */
    Moon.config = function(opts) {
      if(opts.silent) {
        config.silent = opts.silent;
      }
      if(opts.prefix) {
        config.prefix = opts.prefix;
      }
    }

    /**
    * Runs an external Plugin
    * @param {Object} plugin
    */
    Moon.use = function(plugin) {
      plugin.init(Moon);
    }

    /**
    * Creates a Directive
    * @param {String} name
    * @param {Function} action
    */
    Moon.directive = function(name, action) {
      directives["m-" + name] = action;
    }

    /**
    * Creates a Component
    * @param {String} name
    * @param {Function} action
    */
    Moon.component = function(name, opts) {
      var Parent = this;
      function MoonComponent() {
        Moon.call(this, opts);
      }
      MoonComponent.prototype = Object.create(Parent.prototype);
      MoonComponent.prototype.constructor = MoonComponent;
      var component = new MoonComponent();
      components[name] = component;
      return component;
    }

    window.Moon = Moon;
})(window);
<commit_msg>build<commit_after>/*
* Moon 0.1.3
* Copyright 2016, Kabir Shah
* https://github.com/KingPixil/moon/
* Free to use under the MIT license.
* https://kingpixil.github.io/license
*/

"use strict";
(function(window) {

    /* ======= Global Variables ======= */
    var config = {
      silent: false
    }
    var directives = {};
    var components = {};

    /* ======= Global Utilities ======= */

    /**
    * Converts attributes into key-value pairs
    * @param {Node} node
    * @return {Object} Key-Value pairs of Attributes
    */
    var extractAttrs = function(node) {
      var attrs = {};
      if(!node.attributes) return attrs;
      var rawAttrs = node.attributes;
      for(var i = 0; i < rawAttrs.length; i++) {
        attrs[rawAttrs[i].name] = rawAttrs[i].value
      }

      return attrs;
    }

    /**
    * Compiles a template with given data
    * @param {String} template
    * @param {Object} data
    * @return {String} Template with data rendered
    */
    var compileTemplate = function(template, data) {
      var code = template,
          re = /{{([A-Za-z0-9_.()\[\]]+)}}/gi;
      code.replace(re, function(match, p) {
        code = code.replace(match, "` + data." + p + " + `");
      });
      var compile = new Function("data", "var out = `" + code + "`; return out");
      var output = compile(data);
      return output;
    }

    /**
    * Gets Root Element
    * @param {String} html
    * @return {Node} Root Element
    */
    var getRootElement = function(html) {
      var dummy = document.createElement('div');
      dummy.innerHTML = html;
      return dummy.firstChild;
    }

    /**
    * Merges two Objects
    * @param {Object} obj
    * @param {Object} obj2
    * @return {Object} Merged Objects
    */
    function merge(obj, obj2) {
      for (var key in obj2) {
        if (obj2.hasOwnProperty(key)) obj[key] = obj2[key];
      }
      return obj;
    }

    /**
    * Creates an object to be used in a Virtual DOM
    * @param {String} type
    * @param {Array} children
    * @param {String} val
    * @param {Object} props
    * @param {Node} node
    * @return {Object} Object usable in Virtual DOM
    */
    var createElement = function(type, children, val, props, node) {
      return {type: type, children: children, val: val, props: props, node: node};
    }

    /**
    * Create Elements Recursively For all Children
    * @param {Array} children
    * @return {Array} Array of elements usable in Virtual DOM
    */
    var recursiveChildren = function(children) {
      var recursiveChildrenArr = [];
      for(var i = 0; i < children.length; i++) {
        var child = children[i];
        recursiveChildrenArr.push(createElement(child.nodeName, recursiveChildren(child.childNodes), child.textContent, extractAttrs(child), child));
      }
      return recursiveChildrenArr;
    }

    /**
    * Creates Virtual DOM
    * @param {Node} node
    * @return {Object} Virtual DOM
    */
    var createVirtualDOM = function(node) {
      var vdom = createElement(node.nodeName, recursiveChildren(node.childNodes), node.textContent, extractAttrs(node), node);
      return vdom;
    }

    /**
     * Compiles JSX to HTML
     * @param {String} tag
     * @param {Object} attrs
     * @param {Array} children
     * @return {String} HTML compiled from JSX
     */
     var h = function() {
     	 var args = Array.prototype.slice.call(arguments);
     	 var tag = args.shift();
       var attrs = args.shift() || {};
       var kids = args;
       var formattedAttrs = Object.keys(attrs).reduce(function(all, attr) {
       		return all + " " + attr + "='" + attrs[attr] + "'";
       }, '');
       var startTag = "<" + tag + formattedAttrs + ">";
       var endTag = "</" + tag + ">";
       var html = startTag + kids.join("") + endTag;
     	 return html;
     };

    /**
     * Sets the Elements Initial Value
     * @param {Node} el
     * @param {String} value
     */
    var setInitialElementValue = function(el, value) {
      el.innerHTML = value;
    }

    /**
     * Does No Operation
     */
    var noop = function() {

    }

    function Moon(opts) {
        /* ======= Initial Values ======= */
        var _el = opts.el;
        var _data = opts.data;
        var self = this;
        this.$el = document.querySelector(_el);
        this.$template = opts.template || this.$el.innerHTML;
        this.$hooks = merge({created: noop, mounted: noop, updated: noop, destroyed: noop}, opts.hooks);
        this.$methods = opts.methods || {};
        this.$components = merge(opts.components || {}, components);
        this.$dom = {type: this.$el.nodeName, children: [], node: this.$el};
        this.$destroyed = false;

        /* ======= Listen for Changes ======= */
        Object.defineProperty(this, '$data', {
            get: function() {
                return _data;
            },
            set: function(value) {
                _data = value;
                this.build(this.$dom.children);
            },
            configurable: true
        });

        /* ======= Default Directives ======= */
        directives["m-if"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.textContent = "";
          } else {
            el.textContent = compileTemplate(vdom.val, self.$data);
          }
        }

        directives["m-show"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.style.display = 'none';
          } else {
            el.style.display = 'block';
          }
        }

        directives["m-on"] = function(el, val, vdom) {
          var splitVal = val.split(":");
          var eventToCall = splitVal[0];
          var methodToCall = splitVal[1];
          el.addEventListener(eventToCall, function() {
            self.method(methodToCall);
          });
          delete vdom.props["m-on"];
        }

        directives["m-model"] = function(el, val, vdom) {
          el.value = self.get(val);
          el.addEventListener("input", function() {
            self.set(val, el.value);
          });
          delete vdom.props["m-model"];
        }

        directives["m-for"] = function(el, val, vdom) {
          var parts = val.split(" in ");
          var alias = parts[0];
          var array = self.get(parts[1]);
        }

        directives["m-once"] = function(el, val, vdom) {
          vdom.val = el.textContent;
          for(var child in vdom.children) {
            vdom.children[child].val = compileTemplate(vdom.children[child].val, self.$data);
          }
        }

        directives["m-text"] = function(el, val, vdom) {
          el.textContent = val;
        }

        directives["m-html"] = function(el, val, vdom) {
          el.innerHTML = val;
        }

        directives["m-mask"] = function(el, val, vdom) {

        }

        /* ======= Initialize 🎉 ======= */
        this.init();
    }

    /* ======= Instance Methods ======= */

    var hasConsole = typeof window.console !== undefined;

    /**
    * Logs a Message
    * @param {String} msg
    */
    Moon.prototype.log = function(msg) {
      if(!config.silent && hasConsole) console.log(msg);
    }

    /**
    * Throws an Error
    * @param {String} msg
    */
    Moon.prototype.error = function(msg) {
      if(hasConsole) console.error("[Moon] ERR: " + msg);
    }

    /**
    * Sets Value in Data
    * @param {String} key
    * @param {String} val
    */
    Moon.prototype.set = function(key, val) {
      this.$data[key] = val;
      if(!this.$destroyed) this.build(this.$dom.children);
      this.$hooks.updated();
    }

    /**
    * Gets Value in Data
    * @param {String} key
    * @return {String} Value of key in data
    */
    Moon.prototype.get = function(key) {
      return this.$data[key];
    }

    /**
    * Calls a method
    * @param {String} method
    */
    Moon.prototype.method = function(method) {
      this.$methods[method]();
    }

    /**
    * Destroys Moon Instance
    */
    Moon.prototype.destroy = function() {
      Object.defineProperty(this, '$data', {
        set: function(value) {
          _data = value;
        }
      });
      this.$destroyed = true;
      this.$hooks.destroyed();
    }

    /**
    * Builds the DOM With Data
    * @param {Array} children
    */
    Moon.prototype.build = function(children) {
      for(var i = 0; i < children.length; i++) {
        var el = children[i];

        if(el.type === "#text") {
          el.node.textContent = compileTemplate(el.val, this.$data);
        } else if(el.props) {
          for(var prop in el.props) {
            var propVal = el.props[prop];
            var compiledProperty = compileTemplate(propVal, this.$data);
            var directive = directives[prop];
            if(directive) {
              el.node.removeAttribute(prop);
              directive(el.node, compiledProperty, el);
            }

            if(!directive) el.node.setAttribute(prop, compiledProperty);
          }
        }

        this.build(el.children);
      }
    }

    /**
    * Initializes Moon
    */
    Moon.prototype.init = function() {
      this.log("======= Moon =======");
      this.$hooks.created();
      setInitialElementValue(this.$el, this.$template);
      this.$dom = createVirtualDOM(this.$el);
      this.build(this.$dom.children);
      this.$hooks.mounted();
    }

    /* ======= Global API ======= */

    /**
    * Sets the Configuration of Moon
    * @param {Object} opts
    */
    Moon.config = function(opts) {
      if(opts.silent) {
        config.silent = opts.silent;
      }
      if(opts.prefix) {
        config.prefix = opts.prefix;
      }
    }

    /**
    * Runs an external Plugin
    * @param {Object} plugin
    */
    Moon.use = function(plugin) {
      plugin.init(Moon);
    }

    /**
    * Creates a Directive
    * @param {String} name
    * @param {Function} action
    */
    Moon.directive = function(name, action) {
      directives["m-" + name] = action;
    }

    /**
    * Creates a Component
    * @param {String} name
    * @param {Function} action
    */
    Moon.component = function(name, opts) {
      var Parent = this;
      function MoonComponent() {
        Moon.call(this, opts);
      }
      MoonComponent.prototype = Object.create(Parent.prototype);
      MoonComponent.prototype.constructor = MoonComponent;
      var component = new MoonComponent();
      components[name] = component;
      return component;
    }

    window.Moon = Moon;
})(window);
<|endoftext|>


<commit_before>/**
 * @author Mat Groves http://matgroves.com/ @Doormat23
 */

/**
 * the CanvasRenderer draws the stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL.
 * Dont forget to add the view to your DOM or you will not see anything :)
 *
 * @class CanvasRenderer
 * @constructor
 * @param width=800 {Number} the width of the canvas view
 * @param height=600 {Number} the height of the canvas view
 * @param [view] {HTMLCanvasElement} the canvas to use as a view, optional
 * @param [transparent=false] {Boolean} the transparency of the render view, default false
 */
PIXI.CanvasRenderer = function(width, height, view, transparent)
{
    PIXI.defaultRenderer = PIXI.defaultRenderer || this;

    this.type = PIXI.CANVAS_RENDERER;

    /**
     * This sets if the CanvasRenderer will clear the canvas or not before the new render pass.
     * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color.
     * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame.
     * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set.
     *
     * @property clearBeforeRender
     * @type Boolean
     * @default
     */
    this.clearBeforeRender = true;

    /**
     * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation.
     * Handy for crisp pixel art and speed on legacy devices.
     *
     * @property roundPixels
     * @type Boolean
     * @default
     */
    this.roundPixels = false;

    /**
     * Whether the render view is transparent
     *
     * @property transparent
     * @type Boolean
     */
    this.transparent = !!transparent;

    if(!PIXI.blendModesCanvas)
    {
        PIXI.blendModesCanvas = [];

        if(PIXI.canUseNewCanvasBlendModes())
        {
            PIXI.blendModesCanvas[PIXI.blendModes.NORMAL]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.ADD]      = "lighter"; //IS THIS OK???
            PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "multiply";
            PIXI.blendModesCanvas[PIXI.blendModes.SCREEN]   = "screen";
            PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY]  = "overlay";
            PIXI.blendModesCanvas[PIXI.blendModes.DARKEN]   = "darken";
            PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN]  = "lighten";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "color-dodge";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "color-burn";
            PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "hard-light";
            PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "soft-light";
            PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "difference";
            PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "exclusion";
            PIXI.blendModesCanvas[PIXI.blendModes.HUE]       = "hue";
            PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "saturation";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR]      = "color";
            PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "luminosity";
        }
        else
        {
            // this means that the browser does not support the cool new blend modes in canvas "cough" ie "cough"
            PIXI.blendModesCanvas[PIXI.blendModes.NORMAL]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.ADD]      = "lighter"; //IS THIS OK???
            PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.SCREEN]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY]  = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.DARKEN]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN]  = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.HUE]       = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR]      = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "source-over";
        }
    }

    /**
     * The width of the canvas view
     *
     * @property width
     * @type Number
     * @default 800
     */
    this.width = width || 800;

    /**
     * The height of the canvas view
     *
     * @property height
     * @type Number
     * @default 600
     */
    this.height = height || 600;

    /**
     * The canvas element that everything is drawn to
     *
     * @property view
     * @type HTMLCanvasElement
     */
    this.view = view || document.createElement( "canvas" );

    /**
     * The canvas 2d context that everything is drawn with
     * @property context
     * @type HTMLCanvasElement 2d Context
     */
    this.context = this.view.getContext( "2d", { alpha: this.transparent } );

    this.refresh = true;
    // hack to enable some hardware acceleration!
    //this.view.style["transform"] = "translatez(0)";

    this.view.width = this.width;
    this.view.height = this.height;
    this.count = 0;

    /**
     * Instance of a PIXI.CanvasMaskManager, handles masking when using the canvas renderer
     * @property CanvasMaskManager
     * @type CanvasMaskManager
     */
    this.maskManager = new PIXI.CanvasMaskManager();

    /**
     * The render session is just a bunch of parameter used for rendering
     * @property renderSession
     * @type Object
     */
    this.renderSession = {
        context: this.context,
        maskManager: this.maskManager,
        scaleMode: null,
        smoothProperty: null
    };

    if("imageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "imageSmoothingEnabled";
    else if("webkitImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "webkitImageSmoothingEnabled";
    else if("mozImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "mozImageSmoothingEnabled";
    else if("oImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "oImageSmoothingEnabled";
};

// constructor
PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer;

/**
 * Renders the stage to its canvas view
 *
 * @method render
 * @param stage {Stage} the Stage element to be rendered
 */
PIXI.CanvasRenderer.prototype.render = function(stage)
{
    // update textures if need be
    PIXI.texturesToUpdate.length = 0;
    PIXI.texturesToDestroy.length = 0;

    stage.updateTransform();

    this.context.setTransform(1,0,0,1,0,0);
    this.context.globalAlpha = 1;

    if (!this.transparent && this.clearBeforeRender)
    {
        this.context.fillStyle = stage.backgroundColorString;
        this.context.fillRect(0, 0, this.width, this.height);
    }
    else if (this.transparent && this.clearBeforeRender)
    {
        this.context.clearRect(0, 0, this.width, this.height);
    }

    this.renderDisplayObject(stage);

    // run interaction!
    if(stage.interactive)
    {
        //need to add some events!
        if(!stage._interactiveEventsAdded)
        {
            stage._interactiveEventsAdded = true;
            stage.interactionManager.setTarget(this);
        }
    }

    // remove frame updates..
    if(PIXI.Texture.frameUpdates.length > 0)
    {
        PIXI.Texture.frameUpdates.length = 0;
    }
};

/**
 * Resizes the canvas view to the specified width and height
 *
 * @method resize
 * @param width {Number} the new width of the canvas view
 * @param height {Number} the new height of the canvas view
 */
PIXI.CanvasRenderer.prototype.resize = function(width, height)
{
    this.width = width;
    this.height = height;

    this.view.width = width;
    this.view.height = height;
};

/**
 * Renders a display object
 *
 * @method renderDisplayObject
 * @param displayObject {DisplayObject} The displayObject to render
 * @param context {Context2D} the context 2d method of the canvas
 * @private
 */
PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context)
{
    // no longer recursive!
    //var transform;
    //var context = this.context;

    this.renderSession.context = context || this.context;
    displayObject._renderCanvas(this.renderSession);
};

/**
 * Renders a flat strip
 *
 * @method renderStripFlat
 * @param strip {Strip} The Strip to render
 * @private
 */
PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip)
{
    var context = this.context;
    var verticies = strip.verticies;

    var length = verticies.length/2;
    this.count++;

    context.beginPath();
    for (var i=1; i < length-2; i++)
    {
        // draw some triangles!
        var index = i*2;

        var x0 = verticies[index],   x1 = verticies[index+2], x2 = verticies[index+4];
        var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5];

        context.moveTo(x0, y0);
        context.lineTo(x1, y1);
        context.lineTo(x2, y2);
    }

    context.fillStyle = "#FF0000";
    context.fill();
    context.closePath();
};

/**
 * Renders a strip
 *
 * @method renderStrip
 * @param strip {Strip} The Strip to render
 * @private
 */
PIXI.CanvasRenderer.prototype.renderStrip = function(strip)
{
    var context = this.context;

    // draw triangles!!
    var verticies = strip.verticies;
    var uvs = strip.uvs;

    var length = verticies.length/2;
    this.count++;

    for (var i = 1; i < length-2; i++)
    {
        // draw some triangles!
        var index = i*2;

        var x0 = verticies[index],   x1 = verticies[index+2], x2 = verticies[index+4];
        var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5];

        var u0 = uvs[index] * strip.texture.width,   u1 = uvs[index+2] * strip.texture.width, u2 = uvs[index+4]* strip.texture.width;
        var v0 = uvs[index+1]* strip.texture.height, v1 = uvs[index+3] * strip.texture.height, v2 = uvs[index+5]* strip.texture.height;

        context.save();
        context.beginPath();
        context.moveTo(x0, y0);
        context.lineTo(x1, y1);
        context.lineTo(x2, y2);
        context.closePath();

        context.clip();

        // Compute matrix transform
        var delta = u0*v1 + v0*u2 + u1*v2 - v1*u2 - v0*u1 - u0*v2;
        var deltaA = x0*v1 + v0*x2 + x1*v2 - v1*x2 - v0*x1 - x0*v2;
        var deltaB = u0*x1 + x0*u2 + u1*x2 - x1*u2 - x0*u1 - u0*x2;
        var deltaC = u0*v1*x2 + v0*x1*u2 + x0*u1*v2 - x0*v1*u2 - v0*u1*x2 - u0*x1*v2;
        var deltaD = y0*v1 + v0*y2 + y1*v2 - v1*y2 - v0*y1 - y0*v2;
        var deltaE = u0*y1 + y0*u2 + u1*y2 - y1*u2 - y0*u1 - u0*y2;
        var deltaF = u0*v1*y2 + v0*y1*u2 + y0*u1*v2 - y0*v1*u2 - v0*u1*y2 - u0*y1*v2;

        context.transform(deltaA / delta, deltaD / delta,
                            deltaB / delta, deltaE / delta,
                            deltaC / delta, deltaF / delta);

        context.drawImage(strip.texture.baseTexture.source, 0, 0);
        context.restore();
    }
};

/**
 * Creates a Canvas element of the given size
 *
 * @method CanvasBuffer
 * @param width {Number} the width for the newly created canvas
 * @param height {Number} the height for the newly created canvas
 * @static
 * @private
 */
PIXI.CanvasBuffer = function(width, height)
{
    this.width = width;
    this.height = height;

    this.canvas = document.createElement( "canvas" );
    this.context = this.canvas.getContext( "2d" );

    this.canvas.width = width;
    this.canvas.height = height;
};

/**
 * Clears the canvas that was created by the CanvasBuffer class
 *
 * @method clear
 * @private
 */
PIXI.CanvasBuffer.prototype.clear = function()
{
    this.context.clearRect(0,0, this.width, this.height);
};

/**
 * Resizes the canvas that was created by the CanvasBuffer class to the specified width and height
 *
 * @method resize
 * @param width {Number} the new width of the canvas
 * @param height {Number} the new height of the canvas
 * @private
 */

PIXI.CanvasBuffer.prototype.resize = function(width, height)
{
    this.width = this.canvas.width = width;
    this.height = this.canvas.height = height;
};

<commit_msg>CocoonJS compatibility fix. Screencanvas cleared before painting.<commit_after>/**
 * @author Mat Groves http://matgroves.com/ @Doormat23
 */

/**
 * the CanvasRenderer draws the stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL.
 * Dont forget to add the view to your DOM or you will not see anything :)
 *
 * @class CanvasRenderer
 * @constructor
 * @param width=800 {Number} the width of the canvas view
 * @param height=600 {Number} the height of the canvas view
 * @param [view] {HTMLCanvasElement} the canvas to use as a view, optional
 * @param [transparent=false] {Boolean} the transparency of the render view, default false
 */
PIXI.CanvasRenderer = function(width, height, view, transparent)
{
    PIXI.defaultRenderer = PIXI.defaultRenderer || this;

    this.type = PIXI.CANVAS_RENDERER;

    /**
     * This sets if the CanvasRenderer will clear the canvas or not before the new render pass.
     * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color.
     * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame.
     * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set.
     *
     * @property clearBeforeRender
     * @type Boolean
     * @default
     */
    this.clearBeforeRender = true;

    /**
     * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation.
     * Handy for crisp pixel art and speed on legacy devices.
     *
     * @property roundPixels
     * @type Boolean
     * @default
     */
    this.roundPixels = false;

    /**
     * Whether the render view is transparent
     *
     * @property transparent
     * @type Boolean
     */
    this.transparent = !!transparent;

    if(!PIXI.blendModesCanvas)
    {
        PIXI.blendModesCanvas = [];

        if(PIXI.canUseNewCanvasBlendModes())
        {
            PIXI.blendModesCanvas[PIXI.blendModes.NORMAL]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.ADD]      = "lighter"; //IS THIS OK???
            PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "multiply";
            PIXI.blendModesCanvas[PIXI.blendModes.SCREEN]   = "screen";
            PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY]  = "overlay";
            PIXI.blendModesCanvas[PIXI.blendModes.DARKEN]   = "darken";
            PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN]  = "lighten";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "color-dodge";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "color-burn";
            PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "hard-light";
            PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "soft-light";
            PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "difference";
            PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "exclusion";
            PIXI.blendModesCanvas[PIXI.blendModes.HUE]       = "hue";
            PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "saturation";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR]      = "color";
            PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "luminosity";
        }
        else
        {
            // this means that the browser does not support the cool new blend modes in canvas "cough" ie "cough"
            PIXI.blendModesCanvas[PIXI.blendModes.NORMAL]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.ADD]      = "lighter"; //IS THIS OK???
            PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.SCREEN]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY]  = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.DARKEN]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN]  = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.HUE]       = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR]      = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "source-over";
        }
    }

    /**
     * The width of the canvas view
     *
     * @property width
     * @type Number
     * @default 800
     */
    this.width = width || 800;

    /**
     * The height of the canvas view
     *
     * @property height
     * @type Number
     * @default 600
     */
    this.height = height || 600;

    /**
     * The canvas element that everything is drawn to
     *
     * @property view
     * @type HTMLCanvasElement
     */
    this.view = view || document.createElement( "canvas" );

    /**
     * The canvas 2d context that everything is drawn with
     * @property context
     * @type HTMLCanvasElement 2d Context
     */
    this.context = this.view.getContext( "2d", { alpha: this.transparent } );

    this.refresh = true;
    // hack to enable some hardware acceleration!
    //this.view.style["transform"] = "translatez(0)";

    this.view.width = this.width;
    this.view.height = this.height;
    this.count = 0;

    /**
     * Instance of a PIXI.CanvasMaskManager, handles masking when using the canvas renderer
     * @property CanvasMaskManager
     * @type CanvasMaskManager
     */
    this.maskManager = new PIXI.CanvasMaskManager();

    /**
     * The render session is just a bunch of parameter used for rendering
     * @property renderSession
     * @type Object
     */
    this.renderSession = {
        context: this.context,
        maskManager: this.maskManager,
        scaleMode: null,
        smoothProperty: null
    };

    if("imageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "imageSmoothingEnabled";
    else if("webkitImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "webkitImageSmoothingEnabled";
    else if("mozImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "mozImageSmoothingEnabled";
    else if("oImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "oImageSmoothingEnabled";
};

// constructor
PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer;

/**
 * Renders the stage to its canvas view
 *
 * @method render
 * @param stage {Stage} the Stage element to be rendered
 */
PIXI.CanvasRenderer.prototype.render = function(stage)
{
    // update textures if need be
    PIXI.texturesToUpdate.length = 0;
    PIXI.texturesToDestroy.length = 0;

    stage.updateTransform();

    this.context.setTransform(1,0,0,1,0,0);
    this.context.globalAlpha = 1;

    if (navigator.isCocoonJS && this.view.screencanvas) {
        this.context.fillStyle = "black";
        this.context.clear();
    }

    if (!this.transparent && this.clearBeforeRender)
    {
        this.context.fillStyle = stage.backgroundColorString;
        this.context.fillRect(0, 0, this.width, this.height);
    }
    else if (this.transparent && this.clearBeforeRender)
    {
        this.context.clearRect(0, 0, this.width, this.height);
    }

    this.renderDisplayObject(stage);

    // run interaction!
    if(stage.interactive)
    {
        //need to add some events!
        if(!stage._interactiveEventsAdded)
        {
            stage._interactiveEventsAdded = true;
            stage.interactionManager.setTarget(this);
        }
    }

    // remove frame updates..
    if(PIXI.Texture.frameUpdates.length > 0)
    {
        PIXI.Texture.frameUpdates.length = 0;
    }
};

/**
 * Resizes the canvas view to the specified width and height
 *
 * @method resize
 * @param width {Number} the new width of the canvas view
 * @param height {Number} the new height of the canvas view
 */
PIXI.CanvasRenderer.prototype.resize = function(width, height)
{
    this.width = width;
    this.height = height;

    this.view.width = width;
    this.view.height = height;
};

/**
 * Renders a display object
 *
 * @method renderDisplayObject
 * @param displayObject {DisplayObject} The displayObject to render
 * @param context {Context2D} the context 2d method of the canvas
 * @private
 */
PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context)
{
    // no longer recursive!
    //var transform;
    //var context = this.context;

    this.renderSession.context = context || this.context;
    displayObject._renderCanvas(this.renderSession);
};

/**
 * Renders a flat strip
 *
 * @method renderStripFlat
 * @param strip {Strip} The Strip to render
 * @private
 */
PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip)
{
    var context = this.context;
    var verticies = strip.verticies;

    var length = verticies.length/2;
    this.count++;

    context.beginPath();
    for (var i=1; i < length-2; i++)
    {
        // draw some triangles!
        var index = i*2;

        var x0 = verticies[index],   x1 = verticies[index+2], x2 = verticies[index+4];
        var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5];

        context.moveTo(x0, y0);
        context.lineTo(x1, y1);
        context.lineTo(x2, y2);
    }

    context.fillStyle = "#FF0000";
    context.fill();
    context.closePath();
};

/**
 * Renders a strip
 *
 * @method renderStrip
 * @param strip {Strip} The Strip to render
 * @private
 */
PIXI.CanvasRenderer.prototype.renderStrip = function(strip)
{
    var context = this.context;

    // draw triangles!!
    var verticies = strip.verticies;
    var uvs = strip.uvs;

    var length = verticies.length/2;
    this.count++;

    for (var i = 1; i < length-2; i++)
    {
        // draw some triangles!
        var index = i*2;

        var x0 = verticies[index],   x1 = verticies[index+2], x2 = verticies[index+4];
        var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5];

        var u0 = uvs[index] * strip.texture.width,   u1 = uvs[index+2] * strip.texture.width, u2 = uvs[index+4]* strip.texture.width;
        var v0 = uvs[index+1]* strip.texture.height, v1 = uvs[index+3] * strip.texture.height, v2 = uvs[index+5]* strip.texture.height;

        context.save();
        context.beginPath();
        context.moveTo(x0, y0);
        context.lineTo(x1, y1);
        context.lineTo(x2, y2);
        context.closePath();

        context.clip();

        // Compute matrix transform
        var delta = u0*v1 + v0*u2 + u1*v2 - v1*u2 - v0*u1 - u0*v2;
        var deltaA = x0*v1 + v0*x2 + x1*v2 - v1*x2 - v0*x1 - x0*v2;
        var deltaB = u0*x1 + x0*u2 + u1*x2 - x1*u2 - x0*u1 - u0*x2;
        var deltaC = u0*v1*x2 + v0*x1*u2 + x0*u1*v2 - x0*v1*u2 - v0*u1*x2 - u0*x1*v2;
        var deltaD = y0*v1 + v0*y2 + y1*v2 - v1*y2 - v0*y1 - y0*v2;
        var deltaE = u0*y1 + y0*u2 + u1*y2 - y1*u2 - y0*u1 - u0*y2;
        var deltaF = u0*v1*y2 + v0*y1*u2 + y0*u1*v2 - y0*v1*u2 - v0*u1*y2 - u0*y1*v2;

        context.transform(deltaA / delta, deltaD / delta,
                            deltaB / delta, deltaE / delta,
                            deltaC / delta, deltaF / delta);

        context.drawImage(strip.texture.baseTexture.source, 0, 0);
        context.restore();
    }
};

/**
 * Creates a Canvas element of the given size
 *
 * @method CanvasBuffer
 * @param width {Number} the width for the newly created canvas
 * @param height {Number} the height for the newly created canvas
 * @static
 * @private
 */
PIXI.CanvasBuffer = function(width, height)
{
    this.width = width;
    this.height = height;

    this.canvas = document.createElement( "canvas" );
    this.context = this.canvas.getContext( "2d" );

    this.canvas.width = width;
    this.canvas.height = height;
};

/**
 * Clears the canvas that was created by the CanvasBuffer class
 *
 * @method clear
 * @private
 */
PIXI.CanvasBuffer.prototype.clear = function()
{
    this.context.clearRect(0,0, this.width, this.height);
};

/**
 * Resizes the canvas that was created by the CanvasBuffer class to the specified width and height
 *
 * @method resize
 * @param width {Number} the new width of the canvas
 * @param height {Number} the new height of the canvas
 * @private
 */

PIXI.CanvasBuffer.prototype.resize = function(width, height)
{
    this.width = this.canvas.width = width;
    this.height = this.canvas.height = height;
};

<|endoftext|>


<commit_before>import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import MarkdownElement from 'docs/src/modules/components/MarkdownElement';
import Grid from '@material-ui/core/Grid';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import Popper from '@material-ui/core/Popper';
import Paper from '@material-ui/core/Paper';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import Switch from '@material-ui/core/Switch';
import TextField from '@material-ui/core/TextField';

const useStyles = makeStyles(theme => ({
  root: {
    flexGrow: 1,
  },
  scrollContainer: {
    height: 400,
    overflow: 'auto',
    marginBottom: theme.spacing(3),
  },
  scroll: {
    position: 'relative',
    width: '230%',
    backgroundColor: theme.palette.background.paper,
    height: '230%',
  },
  legend: {
    marginTop: theme.spacing(2),
    maxWidth: 300,
  },
  paper: {
    maxWidth: 400,
    overflow: 'auto',
  },
  select: {
    width: 200,
  },
  popper: {
    zIndex: 1,
    '&[x-placement*="bottom"] $arrow': {
      top: 0,
      left: 0,
      marginTop: '-0.9em',
      width: '3em',
      height: '1em',
      '&::before': {
        borderWidth: '0 1em 1em 1em',
        borderColor: `transparent transparent ${theme.palette.common.white} transparent`,
      },
    },
    '&[x-placement*="top"] $arrow': {
      bottom: 0,
      left: 0,
      marginBottom: '-0.9em',
      width: '3em',
      height: '1em',
      '&::before': {
        borderWidth: '1em 1em 0 1em',
        borderColor: `${theme.palette.common.white} transparent transparent transparent`,
      },
    },
    '&[x-placement*="right"] $arrow': {
      left: 0,
      marginLeft: '-0.9em',
      height: '3em',
      width: '1em',
      '&::before': {
        borderWidth: '1em 1em 1em 0',
        borderColor: `transparent ${theme.palette.common.white} transparent transparent`,
      },
    },
    '&[x-placement*="left"] $arrow': {
      right: 0,
      marginRight: '-0.9em',
      height: '3em',
      width: '1em',
      '&::before': {
        borderWidth: '1em 0 1em 1em',
        borderColor: `transparent transparent transparent ${theme.palette.common.white}`,
      },
    },
  },
  arrow: {
    position: 'absolute',
    fontSize: 7,
    width: '3em',
    height: '3em',
    '&::before': {
      content: '""',
      margin: 'auto',
      display: 'block',
      width: 0,
      height: 0,
      borderStyle: 'solid',
    },
  },
}));

export default function ScrollPlayground() {
  const anchorRef = React.useRef(null);
  const arrowRef = React.useRef(null);

  const [arrow, setArrow] = React.useState(false);
  const [disablePortal, setDisablePortal] = React.useState(false);
  const [flip, setFlip] = React.useState(true);
  const [open, setOpen] = React.useState(false);
  const [placement, setPlacement] = React.useState('bottom');
  const [preventOverflow, setPreventOverflow] = React.useState('scrollParent');

  const handleClickButton = () => {
    setOpen(prevOpen => !prevOpen);
  };

  const centerScroll = element => {
    if (!element) {
      return;
    }

    const container = element.parentElement;
    container.scrollTop = element.clientHeight / 4;
    container.scrollLeft = element.clientWidth / 4;
  };

  const classes = useStyles();

  const code = `
  \`\`\`jsx
  <Popper
    placement="${placement}"
    disablePortal={${disablePortal}}
    modifiers={{
      flip: {
        enabled: ${flip},
      },
      preventOverflow: {
        enabled: ${preventOverflow !== 'disabled'},
        boundariesElement: '${preventOverflow === 'disabled' ? 'scrollParent' : preventOverflow}',
      },
      arrow: {
        enabled: ${arrow},
        element: arrowRef,
      },
    }}
  >
  \`\`\`
  `;
  const id = open ? 'scroll-playground' : null;

  return (

        <Grid
          className={classes.scroll}
          container
          alignItems="center"
          justify="center"
          ref={centerScroll}
        >

            <Button
              ref={anchorRef}
              variant="contained"
              onClick={handleClickButton}
              aria-describedby={id}
            >
              Toggle Popper
            </Button>
            <Typography className={classes.legend}>
              Scroll around this container to experiment with flip and preventOverflow modifiers.
            </Typography>
            <Popper
              id={id}
              open={open}
              anchorEl={anchorRef.current}
              placement={placement}
              disablePortal={disablePortal}
              className={classes.popper}
              modifiers={{
                flip: {
                  enabled: flip,
                },
                arrow: {
                  enabled: arrow,
                  element: arrowRef,
                },
                preventOverflow: {
                  enabled: preventOverflow !== 'disabled',
                  boundariesElement:
                    preventOverflow === 'disabled' ? 'scrollParent' : preventOverflow,
                },
              }}
            >
              {arrow ?  : null}
              <Paper className={classes.paper}>
                <DialogTitle>{"Use Google's location service?"}</DialogTitle>
                <DialogContent>
                  <DialogContentText>Let Google help apps determine location.</DialogContentText>
                </DialogContent>
                <DialogActions>
                  <Button onClick={handleClickButton} color="primary">
                    Disagree
                  </Button>
                  <Button onClick={handleClickButton} color="primary">
                    Agree
                  </Button>
                </DialogActions>
              </Paper>
            </Popper>

        </Grid>

      <Grid container spacing={2}>
        <Grid item xs={12} sm={6}>
          <Typography gutterBottom variant="h6">
            Appearance
          </Typography>

            <TextField
              margin="normal"
              className={classes.select}
              label="Placement"
              select
              InputLabelProps={{ id: 'scroll-playground-placement-label' }}
              SelectProps={{
                native: true,
                inputProps: { 'aria-labelledby': 'scroll-playground-placement-label' },
              }}
              value={placement}
              onChange={setPlacement}
            >
              <option value="top-start">top-start</option>
              <option value="top">top</option>
              <option value="top-end">top-end</option>
              <option value="left-start">left-start</option>
              <option value="left">left</option>
              <option value="left-end">left-end</option>
              <option value="right-start">right-start</option>
              <option value="right">right</option>
              <option value="right-end">right-end</option>
              <option value="bottom-start">bottom-start</option>
              <option value="bottom">bottom</option>
              <option value="bottom-end">bottom-end</option>
            </TextField>

          <FormControlLabel
            control={
              <Switch checked={disablePortal} onChange={setDisablePortal} value="disablePortal" />
            }
            label="Disable portal (the children stay within it's parent DOM hierarchy)"
          />
        </Grid>
        <Grid item xs={12} sm={6}>
          <Typography gutterBottom variant="h6">
            Modifiers (options from Popper.js)
          </Typography>

            <TextField
              margin="normal"
              className={classes.select}
              label="Prevent overflow"
              select
              InputLabelProps={{ id: 'scroll-playground-overflow-label' }}
              SelectProps={{
                native: true,
                inputProps: { 'aria-labelledby': 'scroll-playground-overflow-label' },
              }}
              value={preventOverflow}
              onChange={setPreventOverflow}
            >
              <option value="disabled">disabled</option>
              <option value="scrollParent">scrollParent</option>
              <option value="viewport">viewport</option>
              <option value="window">window</option>
            </TextField>

          <FormControlLabel
            control={<Switch checked={flip} onChange={setFlip} value="flip" />}
            label={[
              'Flip',
              '(flip the popper’s placement when it starts to overlap its reference element)',
            ].join(' ')}
          />
          <FormControlLabel
            control={<Switch checked={arrow} onChange={setArrow} value="arrow" />}
            label="Arrow"
          />
        </Grid>
      </Grid>
      <MarkdownElement text={code} />

  );
}
<commit_msg>[Popper] Fix ScrollPlayground.js demo (#16948)<commit_after>import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import MarkdownElement from 'docs/src/modules/components/MarkdownElement';
import Grid from '@material-ui/core/Grid';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import Popper from '@material-ui/core/Popper';
import Paper from '@material-ui/core/Paper';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import Switch from '@material-ui/core/Switch';
import TextField from '@material-ui/core/TextField';

const useStyles = makeStyles(theme => ({
  root: {
    flexGrow: 1,
  },
  scrollContainer: {
    height: 400,
    overflow: 'auto',
    marginBottom: theme.spacing(3),
  },
  scroll: {
    position: 'relative',
    width: '230%',
    backgroundColor: theme.palette.background.paper,
    height: '230%',
  },
  legend: {
    marginTop: theme.spacing(2),
    maxWidth: 300,
  },
  paper: {
    maxWidth: 400,
    overflow: 'auto',
  },
  select: {
    width: 200,
  },
  popper: {
    zIndex: 1,
    '&[x-placement*="bottom"] $arrow': {
      top: 0,
      left: 0,
      marginTop: '-0.9em',
      width: '3em',
      height: '1em',
      '&::before': {
        borderWidth: '0 1em 1em 1em',
        borderColor: `transparent transparent ${theme.palette.common.white} transparent`,
      },
    },
    '&[x-placement*="top"] $arrow': {
      bottom: 0,
      left: 0,
      marginBottom: '-0.9em',
      width: '3em',
      height: '1em',
      '&::before': {
        borderWidth: '1em 1em 0 1em',
        borderColor: `${theme.palette.common.white} transparent transparent transparent`,
      },
    },
    '&[x-placement*="right"] $arrow': {
      left: 0,
      marginLeft: '-0.9em',
      height: '3em',
      width: '1em',
      '&::before': {
        borderWidth: '1em 1em 1em 0',
        borderColor: `transparent ${theme.palette.common.white} transparent transparent`,
      },
    },
    '&[x-placement*="left"] $arrow': {
      right: 0,
      marginRight: '-0.9em',
      height: '3em',
      width: '1em',
      '&::before': {
        borderWidth: '1em 0 1em 1em',
        borderColor: `transparent transparent transparent ${theme.palette.common.white}`,
      },
    },
  },
  arrow: {
    position: 'absolute',
    fontSize: 7,
    width: '3em',
    height: '3em',
    '&::before': {
      content: '""',
      margin: 'auto',
      display: 'block',
      width: 0,
      height: 0,
      borderStyle: 'solid',
    },
  },
}));

export default function ScrollPlayground() {
  const anchorRef = React.useRef(null);
  const [arrowRef, setArrowRef] = React.useState(null);

  const [arrow, setArrow] = React.useState(false);
  const [disablePortal, setDisablePortal] = React.useState(false);
  const [flip, setFlip] = React.useState(true);
  const [open, setOpen] = React.useState(false);
  const [placement, setPlacement] = React.useState('bottom');
  const [preventOverflow, setPreventOverflow] = React.useState('scrollParent');

  const handleClickButton = () => {
    setOpen(prevOpen => !prevOpen);
  };

  const centerScroll = element => {
    if (!element) {
      return;
    }

    const container = element.parentElement;
    container.scrollTop = element.clientHeight / 4;
    container.scrollLeft = element.clientWidth / 4;
  };

  const classes = useStyles();

  const code = `
  \`\`\`jsx
  <Popper
    placement="${placement}"
    disablePortal={${disablePortal}}
    modifiers={{
      flip: {
        enabled: ${flip},
      },
      preventOverflow: {
        enabled: ${preventOverflow !== 'disabled'},
        boundariesElement: '${preventOverflow === 'disabled' ? 'scrollParent' : preventOverflow}',
      },
      arrow: {
        enabled: ${arrow},
        element: arrowRef,
      },
    }}
  >
  \`\`\`
  `;
  const id = open ? 'scroll-playground' : null;

  return (

        <Grid
          className={classes.scroll}
          container
          alignItems="center"
          justify="center"
          ref={centerScroll}
        >

            <Button
              ref={anchorRef}
              variant="contained"
              onClick={handleClickButton}
              aria-describedby={id}
            >
              Toggle Popper
            </Button>
            <Typography className={classes.legend}>
              Scroll around this container to experiment with flip and preventOverflow modifiers.
            </Typography>
            <Popper
              id={id}
              open={open}
              anchorEl={anchorRef.current}
              placement={placement}
              disablePortal={disablePortal}
              className={classes.popper}
              modifiers={{
                flip: {
                  enabled: flip,
                },
                arrow: {
                  enabled: arrow,
                  element: arrowRef,
                },
                preventOverflow: {
                  enabled: preventOverflow !== 'disabled',
                  boundariesElement:
                    preventOverflow === 'disabled' ? 'scrollParent' : preventOverflow,
                },
              }}
            >
              {arrow ?  : null}
              <Paper className={classes.paper}>
                <DialogTitle>{"Use Google's location service?"}</DialogTitle>
                <DialogContent>
                  <DialogContentText>Let Google help apps determine location.</DialogContentText>
                </DialogContent>
                <DialogActions>
                  <Button onClick={handleClickButton} color="primary">
                    Disagree
                  </Button>
                  <Button onClick={handleClickButton} color="primary">
                    Agree
                  </Button>
                </DialogActions>
              </Paper>
            </Popper>

        </Grid>

      <Grid container spacing={2}>
        <Grid item xs={12} sm={6}>
          <Typography gutterBottom variant="h6">
            Appearance
          </Typography>

            <TextField
              margin="normal"
              className={classes.select}
              label="Placement"
              select
              InputLabelProps={{ id: 'scroll-playground-placement-label' }}
              SelectProps={{
                native: true,
                inputProps: { 'aria-labelledby': 'scroll-playground-placement-label' },
              }}
              value={placement}
              onChange={event => {
                setPlacement(event.target.value);
              }}
            >
              <option value="top-start">top-start</option>
              <option value="top">top</option>
              <option value="top-end">top-end</option>
              <option value="left-start">left-start</option>
              <option value="left">left</option>
              <option value="left-end">left-end</option>
              <option value="right-start">right-start</option>
              <option value="right">right</option>
              <option value="right-end">right-end</option>
              <option value="bottom-start">bottom-start</option>
              <option value="bottom">bottom</option>
              <option value="bottom-end">bottom-end</option>
            </TextField>

          <FormControlLabel
            control={
              <Switch
                checked={disablePortal}
                onChange={event => {
                  setDisablePortal(event.target.checked);
                }}
                value="disablePortal"
              />
            }
            label="Disable portal (the children stay within it's parent DOM hierarchy)"
          />
        </Grid>
        <Grid item xs={12} sm={6}>
          <Typography gutterBottom variant="h6">
            Modifiers (options from Popper.js)
          </Typography>

            <TextField
              margin="normal"
              className={classes.select}
              label="Prevent overflow"
              select
              InputLabelProps={{ id: 'scroll-playground-overflow-label' }}
              SelectProps={{
                native: true,
                inputProps: { 'aria-labelledby': 'scroll-playground-overflow-label' },
              }}
              value={preventOverflow}
              onChange={event => {
                setPreventOverflow(event.target.checked);
              }}
            >
              <option value="disabled">disabled</option>
              <option value="scrollParent">scrollParent</option>
              <option value="viewport">viewport</option>
              <option value="window">window</option>
            </TextField>

          <FormControlLabel
            control={
              <Switch
                checked={flip}
                onChange={event => {
                  setFlip(event.target.checked);
                }}
                value="flip"
              />
            }
            label={[
              'Flip',
              '(flip the popper’s placement when it starts to overlap its reference element)',
            ].join(' ')}
          />
          <FormControlLabel
            control={
              <Switch
                checked={arrow}
                onChange={event => {
                  setArrow(event.target.checked);
                }}
                value="arrow"
              />
            }
            label="Arrow"
          />
        </Grid>
      </Grid>
      <MarkdownElement text={code} />

  );
}
<|endoftext|>


<commit_before>require('babel-core/register');
const PathToRegexp = require('path-to-regexp');
const request = require('request-promise-native');
const fs = require('fs');
const join = require('path').join;

const BASE_DIR = join(__dirname, '..', 'gh-pages');

function generatePaths(routes, params, parent) {
  if (!parent) {
    parent = '/';
  }

  return routes.reduce((acc, curr) => {
    const p = joinSegments(parent, curr.path);
    const routeParams = params  && params[curr.name] ? params[curr.name] : {};
    const paths = compilePath(p, routeParams.params);
    acc = acc.concat(paths);
    if (curr.children) {
      acc = acc.concat(generatePaths(curr.children, routeParams.children, p));
    }
    return acc;
  }, []);
}

function joinSegments(base, newSegment) {
  return base.charAt(base.length-1) === '/'
    ? base + newSegment
    : base + '/' + newSegment;
}

function localURI(path) {
  return `http://localhost:8000/${path}`
}

function compilePath(path, params) {
  if (!params) {
    return path;
  }
  const compiler = PathToRegexp.compile(path);
  return params.map(compiler);
}

function makeDirs(base, dirPath) {
  // save an extra attempt at making the root folder
  if (dirPath.charAt(0) === '/') {
    dirPath = dirPath.slice(1);
  }
  const segments = dirPath.split('/');
  let current = base;
  segments.forEach(segment => {
    current = join(current, segment);
    if (!fs.existsSync(current)) {
      fs.mkdirSync(current);
    }
  });
  return current;
}

module.exports = function generateStaticFiles(routes, params) {
  return Promise.all(
    generatePaths(routes, params).map(p => (
      request(localURI(p))
        .then(html => {
          const outputDir = makeDirs(BASE_DIR, p);
          fs.writeFile(
            join(outputDir, 'index.html'),
            html,
            function(err) {
              if (err) {
                console.error('something went wrong with path', p);
                console.error(err);
              }
            }
          )
          console.log(p);

        })
        .catch(err => {
          throw Error(err);
        })
    ))
  );
}
<commit_msg>(Docs) Fix build paths [ci skip]<commit_after>require('babel-core/register');
const PathToRegexp = require('path-to-regexp');
const request = require('request-promise-native');
const fs = require('fs');
const join = require('path').join;

const BASE_DIR = join(__dirname, '..', 'gh-pages');

function generatePaths(routes, params, parent) {
  if (!parent) {
    parent = '/';
  }

  return routes.reduce((acc, curr) => {
    const p = joinSegments(parent, curr.path);
    const routeParams = params  && params[curr.name] ? params[curr.name] : {};
    const paths = compilePath(p, routeParams.params);
    acc = acc.concat(paths);
    if (curr.children) {
      acc = acc.concat(generatePaths(curr.children, routeParams.children, p));
    }
    return acc;
  }, []);
}

function joinSegments(base, newSegment) {
  return base.charAt(base.length-1) === '/'
    ? base + newSegment
    : base + '/' + newSegment;
}

function localURI(path) {
  return `http://localhost:8000${path}`
}

function compilePath(path, params) {
  if (!params) {
    return path;
  }
  const compiler = PathToRegexp.compile(path);
  return params.map(compiler);
}

function makeDirs(base, dirPath) {
  // save an extra attempt at making the root folder
  if (dirPath.charAt(0) === '/') {
    dirPath = dirPath.slice(1);
  }
  const segments = dirPath.split('/');
  let current = base;
  segments.forEach(segment => {
    current = join(current, segment);
    if (!fs.existsSync(current)) {
      fs.mkdirSync(current);
    }
  });
  return current;
}

module.exports = function generateStaticFiles(routes, params) {
  return Promise.all(
    generatePaths(routes, params).map(p => (
      request(localURI(p))
        .then(html => {
          const outputDir = makeDirs(BASE_DIR, p);
          fs.writeFile(
            join(outputDir, 'index.html'),
            html,
            function(err) {
              if (err) {
                console.error('something went wrong with path', p);
                console.error(err);
              }
            }
          )
          console.log(p);

        })
        .catch(err => {
          throw Error(err);
        })
    ))
  );
}
<|endoftext|>