<commit_before>/*global canny */
/*jslint browser: true*/

/**
 * E.g.: canny-mod="flowControl" canny-var="{'view' : 'viewName'}"
 *
 * you can activate a initial view with a anchor in the URL e.g.: yourdomain.html#viewToShow
 * Or pass a comma separated module list for activate more module #viewToShow,otherView
 *
 * TODO made it possible to summarize views with one identifier.
 * Instead of call: canny.flowControl.show('view1', 'view2', 'view3') call canny.flowControl.show('view').
 *
 * TODO add a hide method that just hide the specific element.
 *
 * TODO handle the fade in and out via CSS classes - and use transitions for it
 */
(function () {
    "use strict";

    /**
     * wraps transitionend event vendor implementation
     */
    function onTransitionEndOnce(node, cb) {
        var event = (function () {
                if (node.style.webkitTransition !== undefined) {
                    return 'webkitTransitionEnd';
                } else if (node.style.transition !== undefined) {
                    return 'transitionend';
                }
            }()),
            listener = function(e) {
                e.target.removeEventListener(e.type, listener);
                cb(e);
            };
        if (event) {
            node.addEventListener(event, listener, false);
        } else {
            cb();
        }
    }

    var flowControlInstance = function (fcInstanceName) {
        var instanceName = fcInstanceName,
            onShowInitialViewComplete = [],
            modViews = {}, // saves module views
            getViewAnchor = function () {
                var hash = location.hash || null,
                    rx = new RegExp('[^a-zA-Z-_,]', 'g'),
                    hashSub;

                if (hash) {
                    hashSub = hash.substr(1);
                    hashSub = hashSub.substring(0, hashSub.search(rx));
                    return hashSub.split(',');
                }

                return hash;
            },
            getAllModuleChildrens = function (cNode) {
                // TODO test selector if we have more than one module in canny-mod
                var children = cNode.querySelectorAll('[canny-mod*=' + instanceName + ']'),
                    fc_childNodes = {};
//                            if (cNode.hasChildNodes()) {
//                                [].slice.call(cNode.children).forEach(findChildren);
//                            }
                [].slice.call(children).forEach(function (mod) {
                    var attrValue, view;
                    // TODO read attributes should be a part of canny functionality
                    attrValue = mod.getAttribute('canny-var').split("\'").join('\"');
                    if (/:/.test(attrValue)) {
                        // could be a JSON
                        view = JSON.parse(attrValue).view;
                    } else {
                        view = attrValue;
                    }
                    fc_childNodes[view] = mod;
                });
                return fc_childNodes;
            },
            /**
             * Each flowControl node will end up in a flowControlModule.
             *
             * @param node
             * @param attr
             * @returns {{hasChildrenWithName: hasChildrenWithName, getViewName: getViewName, show: show, hide: hide, fadeOut: fadeOut, getNode: getNode, fadeIn: fadeIn}}
             */
            flowControlModule = function (node, attr) {
                var flowControlChildNodes = {},
                    async = false,
                    parentViews = fc.getParentNode(attr.view);
                // saves all children in a object
                flowControlChildNodes = getAllModuleChildrens(node);
//                    console.log('flowControlChildNodes:', flowControlChildNodes);
                return {
                    hasChildrenWithName : function (viewName) {
                        return flowControlChildNodes.hasOwnProperty(viewName);
                    },
                    getViewName : function () {
                        return attr.view;
                    },
                    display : function () {
                        // don't call parents
                        // don't fade in
                        node.style.display = '';
                    },
                    show : function (cb) {
                        if (parentViews) {
                            parentViews.forEach(function (fc_module) {
//                                console.log('parentViews', fc_module.getViewName());
                                fc_module.display();
                            });
                        }
                        if (!async && attr.hasOwnProperty('async')) {
                            canny.async.loadHTML(node, {url : attr.async}, function () {
                                if (attr.whisker) {
                                    if (canny.whisker !== undefined) {
                                        canny.whisker.add(node, attr.whisker);
                                    } else {
                                        console.error("flowControl:try execute whisker but no whisker module is registered on canny.")
                                    }
                                }
                                node.style.display = '';
                                cb();
                            });
                            async = true;
                        } else {
                            node.style.display = '';
                            cb && cb();
                        }
                    },
                    hide : function () {
                        node.style.display = 'none';
                    },
                    fadeOut : function (cb) {
                        fc.fadeOut(node, cb || function () {});
                    },
                    getNode : function () {
                        return node;
                    },
                    fadeIn : function (cb) {
                        if (parentViews) {
                            parentViews.forEach(function (fc_module) {
//                                console.log('parentViews', fc_module.getViewName());
                                fc_module.display();
                            });
                        }
                        if (!async && attr.hasOwnProperty('async')) {
                            canny.async.loadHTML(node, {url : attr.async}, function () {
                                if (attr.whisker) {
                                    if (canny.whisker !== undefined) {
                                        canny.whisker.add(node, attr.whisker);
                                    } else {
                                        console.error("flowControl:try execute whisker but no whisker module is registered on canny.");
                                    }
                                }
                                fc.fadeIn(node,  cb || function () {});
                            });
                            async = true;
                        } else {
                            fc.fadeIn(node,  cb || function () {});
                        }
                    }
                };

            },
            showInitialView = getViewAnchor(),
            fc = {
                // get all parent modules from the given viewName
                getParentNode : function (viewName) {
                    var queue = Object.keys(modViews), l, i, parents = [];
                    l = queue.length;
                    for (i = 0; i < l; i++) {
                        // TODO
                        if (viewName !== queue[i] && modViews[queue[i]][0].hasChildrenWithName(viewName)) {
                            parents.push(modViews[queue[i]][0]);
                        }
                    }
                    return parents.length === 0 ? null : parents;
                },
                // passes a view list and complete the list with all parent node names
                addParents : function (views) {
                    var extViews = views, i, l, pNode,
                        pushExtViews = function (name) {
                            if (extViews.indexOf(name) === -1) {
                                extViews.push(name);
                            }
                        },
                        addParentView = function (viewName) {
                            // TODO call ends always with null - viewName is top parent
                            var pViewName = fc.getParentNode(viewName);
//                            console.log('viewName: ' + viewName, 'pViewName ' + pViewName );
                            if (pViewName) {
                                pViewName.forEach(function (fc_module) {
                                    // TODO while has parent add it to the extViews
                                    pushExtViews(fc_module.getViewName());
                                    addParentView(fc_module.getViewName());
                                });
                            }
                        };
                    l = views.length;
                    for (i = 0; i < l; i++) {
                        pNode = fc.getParentNode(views[i]);
                        if (pNode) {
                            pNode.forEach(function (fc_module) {
                                pushExtViews(fc_module.getViewName());
                                // so far we have parents do it recursive
                                // TODO not needed each parent will do it by own -
                                addParentView(fc_module.getViewName());
                            });
                        }
                    }
                    return extViews;
                },
                fadeOut : function (node, cb) {

                    if(node.style.display === 'none') {
                        cb();
                    } else {
                        node.classList.add('c-flowControl');
                        node.classList.add('fade-out');

                        setTimeout(function () {
                            node.style.display = 'none';
                            node.classList.remove('c-flowControl');
                            node.classList.remove('fade-out');
                            cb();
                        }, 300);
                    }

                },
                fadeIn : function (node, cb) {
                    // TODO: fade in does not work properly
                    node.style.display = '';
                    node.classList.add('c-flowControl');
                    node.classList.add('fade-in');

                    setTimeout(function() {
                        node.classList.remove('c-flowControl');
                        node.classList.remove('fade-in');
                        cb();

                        // trigger reflow to fix the black boxes issue FTTWO-1249
                        // TODO: check if this can be avoided or
                        var box = document.querySelector('.t-centerBox-content');
                        if (box) {
                            box.style.opacity = 0.99;
                            setTimeout(function() {
                                box.style.opacity = 1;
                            }, 50);
                        }
                    }, 300);
                }
            },
            ext = {
                /**
                 *
                 * @param node
                 * @param innerNode
                 * @returns {{remove: remove}}
                 */
                progress : function (node, innerNode) {
                    var newNode = document.createElement('div'), centerNode = document.createElement('div'), txtNode;
                    node.style.position = 'relative';
                    newNode.style.opacity = '0.6';
                    newNode.style.backgroundColor = '#666';
                    newNode.style.position = 'absolute';
                    newNode.style.top = 0;
                    newNode.style.left = 0;
                    newNode.style.width = node.offsetWidth + 'px';
                    newNode.style.height = node.offsetHeight + 'px';
                    newNode.style.borderRadius = window.getComputedStyle(node, null).borderRadius;

                    centerNode.style.position = 'absolute';
                    centerNode.style.top = (node.offsetHeight / 2) - 30 + 'px';
                    centerNode.style.width = node.offsetWidth + 'px';
                    centerNode.style.textAlign = 'center';

                    if (innerNode) {
                        centerNode.appendChild(innerNode);
                    }
                    node.appendChild(newNode);
                    node.appendChild(centerNode);
                    return {
                        remove : function (delay, cb) {
                            setTimeout(function () {
                                node.removeChild(newNode);
                                node.removeChild(centerNode);
                                cb && cb();
                            }, delay || 0);
                        },
                        fadeOut : function (delay, cb) {
                            setTimeout(function () {
                                fc.fadeOut(newNode, function () {
                                    node.removeChild(newNode);
                                    node.removeChild(centerNode);
                                    cb && cb();
                                });
                            }, delay || 0);
                        }
                    };
                }
            },
            /**
             *
             * @type {{mod: {}, createNewInstance: createNewInstance, ready: ready, add: add, show: show, fadeIn: fadeIn, showImmediately: showImmediately, overlay: overlay}}
             */
            api = {
                mod : modViews, // part of api
                /**
                 * this method could be used to create new instances of flowControl (only needed if you
                 * load this script directly without require)
                 * @param name (unique module name)
                 **/
                createNewInstance : function (name) {
                    return flowControl(name);
                },
                ready : function () {
                    var modNames, i, l;
                    if (showInitialView) {
                        modNames = Object.keys(modViews);
                        l = modNames.length;
                        // check if showInitialView contains a registered module
                        for (i = 0; i < l; i++) {
                            if (showInitialView.indexOf(modNames[i]) !== -1) {
                                showInitialView.push(function transmitParameters() {
                                    onShowInitialViewComplete.forEach(function(fc) {
                                        fc();
                                    });
                                });
                                api.showImmediately.apply(null, showInitialView);
                                break;
                            }
                        }
                    }
                },
                onShowInitialViewComplete : function(fc) {
                    onShowInitialViewComplete.push(fc);
                },
                /**
                 *
                 * @param node
                 * @param attr {{view:(identifier),}}
                 */
                add : function (node, attr) {    // part of api
                    if (!modViews[attr.view]) {
                        modViews[attr.view] = [];
                    }
                    modViews[attr.view].push(flowControlModule(node, attr));
                },
                /**
                 * @deprecated will handle showImmediately in near future
                 */
                show : function () {
                    api.fadeIn.apply(this, arguments);
                },
                /**
                 * @param name (arguments list of views to show)
                 */
                fadeIn : function (name) {
                    var showMods = [].slice.call(arguments),
                        queue = Object.keys(modViews),
                        queueCount = 0,// = queue.length,
                        fadeIn = function () {
                            showMods.forEach(function (module) {
                                if (modViews.hasOwnProperty(module)) {
                                    modViews[module].forEach(function (obj) {
                                        obj.fadeIn(function () {
                                            // TODO remove
//                                                console.log('FADE IN DONE');
                                            // TODO count callbacks and handle it ?
                                        });
                                    });
                                }
                            });
                            // if last param is function than handle it as callback
                            if (typeof showMods[showMods.length - 1] === 'function') {
                                showMods[showMods.length - 1]();
                            }
                        };
                    showMods = fc.addParents(showMods);
                    queue.forEach(function (view) {
                        queueCount += modViews[view].length;
                    });
                    // iterate over all registered modules
                    queue.forEach(function (view) {
                        // iterate over all instances of the same view
                        modViews[view].forEach(function (obj) {
                            // hide all (except incoming and parents) TODO but only the parents of the module
                            if (showMods.indexOf(view) === -1) {
                                obj.fadeOut(function () {
                                    queueCount--;
                                    if (queueCount <= 0) {
                                        fadeIn();
                                    }
                                });
                            } else {
                                queueCount--;
                                if (queueCount <= 0) {
                                    fadeIn();
                                }
                            }
                        });
                    });
                },
                /**
                 * @deprecated use show instead
                 * @param name
                 */
                showImmediately : function () {    // module specific
                    var showMods = [].slice.call(arguments),
                        queue = Object.keys(modViews),
                        countCb = (function () {
                            var cb, length = 0;
                            // if last param is function than handle it as callback
                            if (typeof showMods[showMods.length - 1] === 'function') {
                                cb = showMods[showMods.length - 1];
                            }
                            return {
                                countUp : function (num) {
                                    length += num;
                                },
                                reduce : function () {
                                    length--;
                                    if (cb && length <= 0) {
                                        cb();
                                    }
                                }
                            };
                        }()),
                        show = function () {
                            showMods.forEach(function (module) {
                                if (modViews.hasOwnProperty(module)) {
                                    countCb.countUp(modViews[module].length);
                                    modViews[module].forEach(function (obj) {
                                        obj.show(countCb.reduce);
                                    });
                                }
                            });
                        };
                    showMods = fc.addParents(showMods);
                    // hide all (except incoming)
                    queue.forEach(function (view) {
                        modViews[view].forEach(function (obj) {
                            if (showMods.indexOf(obj) === -1) {
                                obj.hide();
                            }
                        });
                    });
                    show();
                },
                overlay : function (name) {
                    var node;
                    // it's own module?
                    if (modViews.hasOwnProperty(name)) {
                        node = modViews[name].getNode();
                    } else {
                        node = document.getElementById(name);
                    }

                    return {
                        by : function (name, text) {
                            return ext[name](node, text);
                        }
                    };
                }
            };
        return api;
    },
    flowControl = (function () {
        var instances = {};
        return function (name) {
            var instance,
                def = name || 'flowControl';
            if (instances.hasOwnProperty(def)) {
                instance = instances[def];
            } else {
                instances[def] = flowControlInstance(def);
                instance = instances[def];
            }
            return instance;
        };
    }());
    // export as module or bind to global
    if (typeof module !== 'undefined' && module.hasOwnProperty('exports')) { module.exports = flowControl; } else {canny.add('flowControl', flowControl('flowControl')); }

}());
<commit_msg>remove function name<commit_after>/*global canny */
/*jslint browser: true*/

/**
 * E.g.: canny-mod="flowControl" canny-var="{'view' : 'viewName'}"
 *
 * you can activate a initial view with a anchor in the URL e.g.: yourdomain.html#viewToShow
 * Or pass a comma separated module list for activate more module #viewToShow,otherView
 *
 * TODO made it possible to summarize views with one identifier.
 * Instead of call: canny.flowControl.show('view1', 'view2', 'view3') call canny.flowControl.show('view').
 *
 * TODO add a hide method that just hide the specific element.
 *
 * TODO handle the fade in and out via CSS classes - and use transitions for it
 */
(function () {
    "use strict";

    /**
     * wraps transitionend event vendor implementation
     */
    function onTransitionEndOnce(node, cb) {
        var event = (function () {
                if (node.style.webkitTransition !== undefined) {
                    return 'webkitTransitionEnd';
                } else if (node.style.transition !== undefined) {
                    return 'transitionend';
                }
            }()),
            listener = function(e) {
                e.target.removeEventListener(e.type, listener);
                cb(e);
            };
        if (event) {
            node.addEventListener(event, listener, false);
        } else {
            cb();
        }
    }

    var flowControlInstance = function (fcInstanceName) {
        var instanceName = fcInstanceName,
            onShowInitialViewComplete = [],
            modViews = {}, // saves module views
            getViewAnchor = function () {
                var hash = location.hash || null,
                    rx = new RegExp('[^a-zA-Z-_,]', 'g'),
                    hashSub;

                if (hash) {
                    hashSub = hash.substr(1);
                    hashSub = hashSub.substring(0, hashSub.search(rx));
                    return hashSub.split(',');
                }

                return hash;
            },
            getAllModuleChildrens = function (cNode) {
                // TODO test selector if we have more than one module in canny-mod
                var children = cNode.querySelectorAll('[canny-mod*=' + instanceName + ']'),
                    fc_childNodes = {};
//                            if (cNode.hasChildNodes()) {
//                                [].slice.call(cNode.children).forEach(findChildren);
//                            }
                [].slice.call(children).forEach(function (mod) {
                    var attrValue, view;
                    // TODO read attributes should be a part of canny functionality
                    attrValue = mod.getAttribute('canny-var').split("\'").join('\"');
                    if (/:/.test(attrValue)) {
                        // could be a JSON
                        view = JSON.parse(attrValue).view;
                    } else {
                        view = attrValue;
                    }
                    fc_childNodes[view] = mod;
                });
                return fc_childNodes;
            },
            /**
             * Each flowControl node will end up in a flowControlModule.
             *
             * @param node
             * @param attr
             * @returns {{hasChildrenWithName: hasChildrenWithName, getViewName: getViewName, show: show, hide: hide, fadeOut: fadeOut, getNode: getNode, fadeIn: fadeIn}}
             */
            flowControlModule = function (node, attr) {
                var flowControlChildNodes = {},
                    async = false,
                    parentViews = fc.getParentNode(attr.view);
                // saves all children in a object
                flowControlChildNodes = getAllModuleChildrens(node);
//                    console.log('flowControlChildNodes:', flowControlChildNodes);
                return {
                    hasChildrenWithName : function (viewName) {
                        return flowControlChildNodes.hasOwnProperty(viewName);
                    },
                    getViewName : function () {
                        return attr.view;
                    },
                    display : function () {
                        // don't call parents
                        // don't fade in
                        node.style.display = '';
                    },
                    show : function (cb) {
                        if (parentViews) {
                            parentViews.forEach(function (fc_module) {
//                                console.log('parentViews', fc_module.getViewName());
                                fc_module.display();
                            });
                        }
                        if (!async && attr.hasOwnProperty('async')) {
                            canny.async.loadHTML(node, {url : attr.async}, function () {
                                if (attr.whisker) {
                                    if (canny.whisker !== undefined) {
                                        canny.whisker.add(node, attr.whisker);
                                    } else {
                                        console.error("flowControl:try execute whisker but no whisker module is registered on canny.")
                                    }
                                }
                                node.style.display = '';
                                cb();
                            });
                            async = true;
                        } else {
                            node.style.display = '';
                            cb && cb();
                        }
                    },
                    hide : function () {
                        node.style.display = 'none';
                    },
                    fadeOut : function (cb) {
                        fc.fadeOut(node, cb || function () {});
                    },
                    getNode : function () {
                        return node;
                    },
                    fadeIn : function (cb) {
                        if (parentViews) {
                            parentViews.forEach(function (fc_module) {
//                                console.log('parentViews', fc_module.getViewName());
                                fc_module.display();
                            });
                        }
                        if (!async && attr.hasOwnProperty('async')) {
                            canny.async.loadHTML(node, {url : attr.async}, function () {
                                if (attr.whisker) {
                                    if (canny.whisker !== undefined) {
                                        canny.whisker.add(node, attr.whisker);
                                    } else {
                                        console.error("flowControl:try execute whisker but no whisker module is registered on canny.");
                                    }
                                }
                                fc.fadeIn(node,  cb || function () {});
                            });
                            async = true;
                        } else {
                            fc.fadeIn(node,  cb || function () {});
                        }
                    }
                };

            },
            showInitialView = getViewAnchor(),
            fc = {
                // get all parent modules from the given viewName
                getParentNode : function (viewName) {
                    var queue = Object.keys(modViews), l, i, parents = [];
                    l = queue.length;
                    for (i = 0; i < l; i++) {
                        // TODO
                        if (viewName !== queue[i] && modViews[queue[i]][0].hasChildrenWithName(viewName)) {
                            parents.push(modViews[queue[i]][0]);
                        }
                    }
                    return parents.length === 0 ? null : parents;
                },
                // passes a view list and complete the list with all parent node names
                addParents : function (views) {
                    var extViews = views, i, l, pNode,
                        pushExtViews = function (name) {
                            if (extViews.indexOf(name) === -1) {
                                extViews.push(name);
                            }
                        },
                        addParentView = function (viewName) {
                            // TODO call ends always with null - viewName is top parent
                            var pViewName = fc.getParentNode(viewName);
//                            console.log('viewName: ' + viewName, 'pViewName ' + pViewName );
                            if (pViewName) {
                                pViewName.forEach(function (fc_module) {
                                    // TODO while has parent add it to the extViews
                                    pushExtViews(fc_module.getViewName());
                                    addParentView(fc_module.getViewName());
                                });
                            }
                        };
                    l = views.length;
                    for (i = 0; i < l; i++) {
                        pNode = fc.getParentNode(views[i]);
                        if (pNode) {
                            pNode.forEach(function (fc_module) {
                                pushExtViews(fc_module.getViewName());
                                // so far we have parents do it recursive
                                // TODO not needed each parent will do it by own -
                                addParentView(fc_module.getViewName());
                            });
                        }
                    }
                    return extViews;
                },
                fadeOut : function (node, cb) {

                    if(node.style.display === 'none') {
                        cb();
                    } else {
                        node.classList.add('c-flowControl');
                        node.classList.add('fade-out');

                        setTimeout(function () {
                            node.style.display = 'none';
                            node.classList.remove('c-flowControl');
                            node.classList.remove('fade-out');
                            cb();
                        }, 300);
                    }

                },
                fadeIn : function (node, cb) {
                    // TODO: fade in does not work properly
                    node.style.display = '';
                    node.classList.add('c-flowControl');
                    node.classList.add('fade-in');

                    setTimeout(function() {
                        node.classList.remove('c-flowControl');
                        node.classList.remove('fade-in');
                        cb();

                        // trigger reflow to fix the black boxes issue FTTWO-1249
                        // TODO: check if this can be avoided or
                        var box = document.querySelector('.t-centerBox-content');
                        if (box) {
                            box.style.opacity = 0.99;
                            setTimeout(function() {
                                box.style.opacity = 1;
                            }, 50);
                        }
                    }, 300);
                }
            },
            ext = {
                /**
                 *
                 * @param node
                 * @param innerNode
                 * @returns {{remove: remove}}
                 */
                progress : function (node, innerNode) {
                    var newNode = document.createElement('div'), centerNode = document.createElement('div'), txtNode;
                    node.style.position = 'relative';
                    newNode.style.opacity = '0.6';
                    newNode.style.backgroundColor = '#666';
                    newNode.style.position = 'absolute';
                    newNode.style.top = 0;
                    newNode.style.left = 0;
                    newNode.style.width = node.offsetWidth + 'px';
                    newNode.style.height = node.offsetHeight + 'px';
                    newNode.style.borderRadius = window.getComputedStyle(node, null).borderRadius;

                    centerNode.style.position = 'absolute';
                    centerNode.style.top = (node.offsetHeight / 2) - 30 + 'px';
                    centerNode.style.width = node.offsetWidth + 'px';
                    centerNode.style.textAlign = 'center';

                    if (innerNode) {
                        centerNode.appendChild(innerNode);
                    }
                    node.appendChild(newNode);
                    node.appendChild(centerNode);
                    return {
                        remove : function (delay, cb) {
                            setTimeout(function () {
                                node.removeChild(newNode);
                                node.removeChild(centerNode);
                                cb && cb();
                            }, delay || 0);
                        },
                        fadeOut : function (delay, cb) {
                            setTimeout(function () {
                                fc.fadeOut(newNode, function () {
                                    node.removeChild(newNode);
                                    node.removeChild(centerNode);
                                    cb && cb();
                                });
                            }, delay || 0);
                        }
                    };
                }
            },
            /**
             *
             * @type {{mod: {}, createNewInstance: createNewInstance, ready: ready, add: add, show: show, fadeIn: fadeIn, showImmediately: showImmediately, overlay: overlay}}
             */
            api = {
                mod : modViews, // part of api
                /**
                 * this method could be used to create new instances of flowControl (only needed if you
                 * load this script directly without require)
                 * @param name (unique module name)
                 **/
                createNewInstance : function (name) {
                    return flowControl(name);
                },
                ready : function () {
                    var modNames, i, l;
                    if (showInitialView) {
                        modNames = Object.keys(modViews);
                        l = modNames.length;
                        // check if showInitialView contains a registered module
                        for (i = 0; i < l; i++) {
                            if (showInitialView.indexOf(modNames[i]) !== -1) {
                                showInitialView.push(function () {
                                    onShowInitialViewComplete.forEach(function(fc) {
                                        fc();
                                    });
                                });
                                api.showImmediately.apply(null, showInitialView);
                                break;
                            }
                        }
                    }
                },
                onShowInitialViewComplete : function(fc) {
                    onShowInitialViewComplete.push(fc);
                },
                /**
                 *
                 * @param node
                 * @param attr {{view:(identifier),}}
                 */
                add : function (node, attr) {    // part of api
                    if (!modViews[attr.view]) {
                        modViews[attr.view] = [];
                    }
                    modViews[attr.view].push(flowControlModule(node, attr));
                },
                /**
                 * @deprecated will handle showImmediately in near future
                 */
                show : function () {
                    api.fadeIn.apply(this, arguments);
                },
                /**
                 * @param name (arguments list of views to show)
                 */
                fadeIn : function (name) {
                    var showMods = [].slice.call(arguments),
                        queue = Object.keys(modViews),
                        queueCount = 0,// = queue.length,
                        fadeIn = function () {
                            showMods.forEach(function (module) {
                                if (modViews.hasOwnProperty(module)) {
                                    modViews[module].forEach(function (obj) {
                                        obj.fadeIn(function () {
                                            // TODO remove
//                                                console.log('FADE IN DONE');
                                            // TODO count callbacks and handle it ?
                                        });
                                    });
                                }
                            });
                            // if last param is function than handle it as callback
                            if (typeof showMods[showMods.length - 1] === 'function') {
                                showMods[showMods.length - 1]();
                            }
                        };
                    showMods = fc.addParents(showMods);
                    queue.forEach(function (view) {
                        queueCount += modViews[view].length;
                    });
                    // iterate over all registered modules
                    queue.forEach(function (view) {
                        // iterate over all instances of the same view
                        modViews[view].forEach(function (obj) {
                            // hide all (except incoming and parents) TODO but only the parents of the module
                            if (showMods.indexOf(view) === -1) {
                                obj.fadeOut(function () {
                                    queueCount--;
                                    if (queueCount <= 0) {
                                        fadeIn();
                                    }
                                });
                            } else {
                                queueCount--;
                                if (queueCount <= 0) {
                                    fadeIn();
                                }
                            }
                        });
                    });
                },
                /**
                 * @deprecated use show instead
                 * @param name
                 */
                showImmediately : function () {    // module specific
                    var showMods = [].slice.call(arguments),
                        queue = Object.keys(modViews),
                        countCb = (function () {
                            var cb, length = 0;
                            // if last param is function than handle it as callback
                            if (typeof showMods[showMods.length - 1] === 'function') {
                                cb = showMods[showMods.length - 1];
                            }
                            return {
                                countUp : function (num) {
                                    length += num;
                                },
                                reduce : function () {
                                    length--;
                                    if (cb && length <= 0) {
                                        cb();
                                    }
                                }
                            };
                        }()),
                        show = function () {
                            showMods.forEach(function (module) {
                                if (modViews.hasOwnProperty(module)) {
                                    countCb.countUp(modViews[module].length);
                                    modViews[module].forEach(function (obj) {
                                        obj.show(countCb.reduce);
                                    });
                                }
                            });
                        };
                    showMods = fc.addParents(showMods);
                    // hide all (except incoming)
                    queue.forEach(function (view) {
                        modViews[view].forEach(function (obj) {
                            if (showMods.indexOf(obj) === -1) {
                                obj.hide();
                            }
                        });
                    });
                    show();
                },
                overlay : function (name) {
                    var node;
                    // it's own module?
                    if (modViews.hasOwnProperty(name)) {
                        node = modViews[name].getNode();
                    } else {
                        node = document.getElementById(name);
                    }

                    return {
                        by : function (name, text) {
                            return ext[name](node, text);
                        }
                    };
                }
            };
        return api;
    },
    flowControl = (function () {
        var instances = {};
        return function (name) {
            var instance,
                def = name || 'flowControl';
            if (instances.hasOwnProperty(def)) {
                instance = instances[def];
            } else {
                instances[def] = flowControlInstance(def);
                instance = instances[def];
            }
            return instance;
        };
    }());
    // export as module or bind to global
    if (typeof module !== 'undefined' && module.hasOwnProperty('exports')) { module.exports = flowControl; } else {canny.add('flowControl', flowControl('flowControl')); }

}());
<|endoftext|>


<commit_before>/*
 * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too.
 */

L.Draggable = L.Class.extend({
	includes: L.Mixin.Events,

	statics: {
		START: L.Browser.touch ? 'touchstart' : 'mousedown',
		END: L.Browser.touch ? 'touchend' : 'mouseup',
		MOVE: L.Browser.touch ? 'touchmove' : 'mousemove',
		TAP_TOLERANCE: 15
	},

	initialize: function (element, dragStartTarget, longPress) {
		this._element = element;
		this._dragStartTarget = dragStartTarget || element;
		this._longPress = longPress;
	},

	enable: function () {
		if (this._enabled) {
			return;
		}
		L.DomEvent.on(this._dragStartTarget, L.Draggable.START, this._onDown, this);
		this._enabled = true;
	},

	disable: function () {
		if (!this._enabled) {
			return;
		}
		L.DomEvent.off(this._dragStartTarget, L.Draggable.START, this._onDown);
		this._enabled = false;
		this._moved = false;
	},

	_onDown: function (e) {
		if ((!L.Browser.touch && e.shiftKey) ||
			((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }

		L.DomEvent.preventDefault(e);
		L.DomEvent.stopPropagation(e);

		if (L.Draggable._disabled) { return; }

		this._simulateClick = true;

		if (e.touches && e.touches.length > 1) {
			this._simulateClick = false;
			clearTimeout(this._contextMenuTimeout);
			return;
		}

		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
			el = first.target;

		if (L.Browser.touch && el.tagName.toLowerCase() === 'a') {
			L.DomUtil.addClass(el, 'leaflet-active');
		}

		this._moved = false;
		if (this._moving) {
			return;
		}

		this._startPoint = new L.Point(first.clientX, first.clientY);
		this._startPos = this._newPos = L.DomUtil.getPosition(this._element);

		//Touch contextmenu event emulation
		if (e.touches && e.touches.length === 1 && L.Browser.touch && this._longPress) {
			var self = this;
			this._contextMenuTimeout = setTimeout(function () {
				var dist = (self._newPos && self._newPos.distanceTo(self._startPos)) || 0;

				if (dist < L.Draggable.TAP_TOLERANCE) {
					self._simulateClick = false;
					self._onUp();
					self._simulateEvent('contextmenu', first);
				}
			}, 1000);
		}

		L.DomEvent.on(document, L.Draggable.MOVE, this._onMove, this);
		L.DomEvent.on(document, L.Draggable.END, this._onUp, this);
	},

	_onMove: function (e) {
		if (e.touches && e.touches.length > 1) { return; }

		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
			newPoint = new L.Point(first.clientX, first.clientY),
			diffVec = newPoint.subtract(this._startPoint);

		if (!diffVec.x && !diffVec.y) { return; }

		L.DomEvent.preventDefault(e);

		if (!this._moved) {
			this.fire('dragstart');
			this._moved = true;

			this._startPos = L.DomUtil.getPosition(this._element).subtract(diffVec);

			if (!L.Browser.touch) {
				L.DomUtil.disableTextSelection();
				this._setMovingCursor();
			}
		}

		this._newPos = this._startPos.add(diffVec);
		this._moving = true;

		L.Util.cancelAnimFrame(this._animRequest);
		this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
	},

	_updatePosition: function () {
		this.fire('predrag');
		L.DomUtil.setPosition(this._element, this._newPos);
		this.fire('drag');
	},

	_onUp: function (e) {
		var simulateClickTouch;
		clearTimeout(this._contextMenuTimeout);
		if (this._simulateClick && e.changedTouches) {
			var first = e.changedTouches[0],
				el = first.target,
				dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;

			if (el.tagName.toLowerCase() === 'a') {
				L.DomUtil.removeClass(el, 'leaflet-active');
			}

			if (dist < L.Draggable.TAP_TOLERANCE) {
				simulateClickTouch = first;
			}
		}

		if (!L.Browser.touch) {
			L.DomUtil.enableTextSelection();
			this._restoreCursor();
		}

		L.DomEvent.off(document, L.Draggable.MOVE, this._onMove);
		L.DomEvent.off(document, L.Draggable.END, this._onUp);

		if (this._moved) {
			// ensure drag is not fired after dragend
			L.Util.cancelAnimFrame(this._animRequest);

			this.fire('dragend');
		}
		this._moving = false;

		if (simulateClickTouch) {
			this._moved = false;
			this._simulateEvent('click', simulateClickTouch);
		}
	},

	_setMovingCursor: function () {
		L.DomUtil.addClass(document.body, 'leaflet-dragging');
	},

	_restoreCursor: function () {
		L.DomUtil.removeClass(document.body, 'leaflet-dragging');
	},

	_simulateEvent: function (type, e) {
		var simulatedEvent = document.createEvent('MouseEvents');

		simulatedEvent.initMouseEvent(
				type, true, true, window, 1,
				e.screenX, e.screenY,
				e.clientX, e.clientY,
				false, false, false, false, 0, null);

		e.target.dispatchEvent(simulatedEvent);
	}
});
<commit_msg>Use L.Util.bind instead of var self.<commit_after>/*
 * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too.
 */

L.Draggable = L.Class.extend({
	includes: L.Mixin.Events,

	statics: {
		START: L.Browser.touch ? 'touchstart' : 'mousedown',
		END: L.Browser.touch ? 'touchend' : 'mouseup',
		MOVE: L.Browser.touch ? 'touchmove' : 'mousemove',
		TAP_TOLERANCE: 15
	},

	initialize: function (element, dragStartTarget, longPress) {
		this._element = element;
		this._dragStartTarget = dragStartTarget || element;
		this._longPress = longPress;
	},

	enable: function () {
		if (this._enabled) {
			return;
		}
		L.DomEvent.on(this._dragStartTarget, L.Draggable.START, this._onDown, this);
		this._enabled = true;
	},

	disable: function () {
		if (!this._enabled) {
			return;
		}
		L.DomEvent.off(this._dragStartTarget, L.Draggable.START, this._onDown);
		this._enabled = false;
		this._moved = false;
	},

	_onDown: function (e) {
		if ((!L.Browser.touch && e.shiftKey) ||
			((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }

		L.DomEvent.preventDefault(e);
		L.DomEvent.stopPropagation(e);

		if (L.Draggable._disabled) { return; }

		this._simulateClick = true;

		if (e.touches && e.touches.length > 1) {
			this._simulateClick = false;
			clearTimeout(this._contextMenuTimeout);
			return;
		}

		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
			el = first.target;

		if (L.Browser.touch && el.tagName.toLowerCase() === 'a') {
			L.DomUtil.addClass(el, 'leaflet-active');
		}

		this._moved = false;
		if (this._moving) {
			return;
		}

		this._startPoint = new L.Point(first.clientX, first.clientY);
		this._startPos = this._newPos = L.DomUtil.getPosition(this._element);

		//Touch contextmenu event emulation
		if (e.touches && e.touches.length === 1 && L.Browser.touch && this._longPress) {
			this._contextMenuTimeout = setTimeout(L.Util.bind(function () {
				var dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;

				if (dist < L.Draggable.TAP_TOLERANCE) {
					this._simulateClick = false;
					this._onUp();
					this._simulateEvent('contextmenu', first);
				}
			}, this), 1000);
		}

		L.DomEvent.on(document, L.Draggable.MOVE, this._onMove, this);
		L.DomEvent.on(document, L.Draggable.END, this._onUp, this);
	},

	_onMove: function (e) {
		if (e.touches && e.touches.length > 1) { return; }

		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
			newPoint = new L.Point(first.clientX, first.clientY),
			diffVec = newPoint.subtract(this._startPoint);

		if (!diffVec.x && !diffVec.y) { return; }

		L.DomEvent.preventDefault(e);

		if (!this._moved) {
			this.fire('dragstart');
			this._moved = true;

			this._startPos = L.DomUtil.getPosition(this._element).subtract(diffVec);

			if (!L.Browser.touch) {
				L.DomUtil.disableTextSelection();
				this._setMovingCursor();
			}
		}

		this._newPos = this._startPos.add(diffVec);
		this._moving = true;

		L.Util.cancelAnimFrame(this._animRequest);
		this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
	},

	_updatePosition: function () {
		this.fire('predrag');
		L.DomUtil.setPosition(this._element, this._newPos);
		this.fire('drag');
	},

	_onUp: function (e) {
		var simulateClickTouch;
		clearTimeout(this._contextMenuTimeout);
		if (this._simulateClick && e.changedTouches) {
			var first = e.changedTouches[0],
				el = first.target,
				dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;

			if (el.tagName.toLowerCase() === 'a') {
				L.DomUtil.removeClass(el, 'leaflet-active');
			}

			if (dist < L.Draggable.TAP_TOLERANCE) {
				simulateClickTouch = first;
			}
		}

		if (!L.Browser.touch) {
			L.DomUtil.enableTextSelection();
			this._restoreCursor();
		}

		L.DomEvent.off(document, L.Draggable.MOVE, this._onMove);
		L.DomEvent.off(document, L.Draggable.END, this._onUp);

		if (this._moved) {
			// ensure drag is not fired after dragend
			L.Util.cancelAnimFrame(this._animRequest);

			this.fire('dragend');
		}
		this._moving = false;

		if (simulateClickTouch) {
			this._moved = false;
			this._simulateEvent('click', simulateClickTouch);
		}
	},

	_setMovingCursor: function () {
		L.DomUtil.addClass(document.body, 'leaflet-dragging');
	},

	_restoreCursor: function () {
		L.DomUtil.removeClass(document.body, 'leaflet-dragging');
	},

	_simulateEvent: function (type, e) {
		var simulatedEvent = document.createEvent('MouseEvents');

		simulatedEvent.initMouseEvent(
				type, true, true, window, 1,
				e.screenX, e.screenY,
				e.clientX, e.clientY,
				false, false, false, false, 0, null);

		e.target.dispatchEvent(simulatedEvent);
	}
});
<|endoftext|>


<commit_before>var dateTime = {
	timer : 0,
	now : false,
	gears : null
};

dateTime.update = function(){
	var time = _gmtServer.split( '/' );
	dateTime.now = new Date( Number(time[0]), Number(time[1]-1), Number(time[2]), Number(time[3]), Number(time[4]), ( Number(time[5]) + dateTime.timer ) ) ;
	dateTime.timer++;
	dateTime.gears = setTimeout( function(){ dateTime.update(); } ,1000);
}

// This method will be called by phonegap at the 'resume' event
dateTime.restart = function(){
	dateTime.timer = 0;
	dateTime.now = false;
	var now = new Date();
	_gmtServer = now.getUTCFullYear() + '/' + (now.getUTCMonth()+1) + '/' + now.getUTCDate() + '/' + now.getUTCHours() + '/' + now.getUTCMinutes() + '/' + now.getUTCSeconds();
}

// Update the gmt time based on server
dateTime.reload = function(){
	var url = App.service + 'gmt';
	App.http.get( url, {
		cache: false
	} ).success( function ( json ) {
		_gmtServer = json.gmt;
		dateTime.timer = 0;
		if( dateTime.gears ){
			clearTimeout( dateTime.gears );
		}
		dateTime.update();
		alert( _gmtServer );
		App.rootScope.$broadcast( 'appResume', false );
	} ).error( function(){ window.location.reload(); } );
}

dateTime.toString = function(){
	return dateTime.now.toString( 'dd MMMM yyyy HH:mm:ss' );
}

dateTime.getNow = function(){
	return Date.parse( dateTime.toString() );
}

dateTime.update();<commit_msg>Beta iOS Safari: all open Yale restaurants are closed #2873 - removed the alert<commit_after>var dateTime = {
	timer : 0,
	now : false,
	gears : null
};

dateTime.update = function(){
	var time = _gmtServer.split( '/' );
	dateTime.now = new Date( Number(time[0]), Number(time[1]-1), Number(time[2]), Number(time[3]), Number(time[4]), ( Number(time[5]) + dateTime.timer ) ) ;
	dateTime.timer++;
	dateTime.gears = setTimeout( function(){ dateTime.update(); } ,1000);
}

// This method will be called by phonegap at the 'resume' event
dateTime.restart = function(){
	dateTime.timer = 0;
	dateTime.now = false;
	var now = new Date();
	_gmtServer = now.getUTCFullYear() + '/' + (now.getUTCMonth()+1) + '/' + now.getUTCDate() + '/' + now.getUTCHours() + '/' + now.getUTCMinutes() + '/' + now.getUTCSeconds();
}

// Update the gmt time based on server
dateTime.reload = function(){
	var url = App.service + 'gmt';
	App.http.get( url, {
		cache: false
	} ).success( function ( json ) {
		_gmtServer = json.gmt;
		dateTime.timer = 0;
		if( dateTime.gears ){
			clearTimeout( dateTime.gears );
		}
		dateTime.update();
		App.rootScope.$broadcast( 'appResume', false );
	} ).error( function(){ window.location.reload(); } );
}

dateTime.toString = function(){
	return dateTime.now.toString( 'dd MMMM yyyy HH:mm:ss' );
}

dateTime.getNow = function(){
	return Date.parse( dateTime.toString() );
}

dateTime.update();<|endoftext|>


<commit_before>define([], function () {
    var Util = {};

    // If once is true, after the event has been fired, any further handlers which are
    // registered will fire immediately, and this type of event cannot be fired twice.
    Util.mkEvent = function (once) {
        var handlers = [];
        var fired = false;
        return {
            reg: function (cb) {
                if (once && fired) { return void setTimeout(cb); }
                handlers.push(cb);
            },
            unreg: function (cb) {
                if (handlers.indexOf(cb) === -1) { throw new Error("Not registered"); }
                handlers.splice(handlers.indexOf(cb), 1);
            },
            fire: function () {
                if (once && fired) { return; }
                fired = true;
                var args = Array.prototype.slice.call(arguments);
                handlers.forEach(function (h) { h.apply(null, args); });
            }
        };
    };

    Util.find = function (map, path) {
        var l = path.length;
        for (var i = 0; i < l; i++) {
            if (typeof(map[path[i]]) === 'undefined') { return; }
            map = map[path[i]];
        }
        return map;
    };

    Util.uid = function () {
        return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
            .toString(32).replace(/\./g, '');
    };

    Util.fixHTML = function (str) {
        if (!str) { return ''; }
        return str.replace(/[<>&"']/g, function (x) {
            return ({ "<": "<", ">": "&gt", "&": "&", '"': """, "'": "'" })[x];
        });
    };

    Util.hexToBase64 = function (hex) {
        var hexArray = hex
            .replace(/\r|\n/g, "")
            .replace(/([\da-fA-F]{2}) ?/g, "0x$1 ")
            .replace(/ +$/, "")
            .split(" ");
        var byteString = String.fromCharCode.apply(null, hexArray);
        return window.btoa(byteString).replace(/\//g, '-').replace(/=+$/, '');
    };

    Util.base64ToHex = function (b64String) {
        var hexArray = [];
        atob(b64String.replace(/-/g, '/')).split("").forEach(function(e){
            var h = e.charCodeAt(0).toString(16);
            if (h.length === 1) { h = "0"+h; }
            hexArray.push(h);
        });
        return hexArray.join("");
    };

    Util.uint8ArrayToHex = function (a) {
        // call slice so Uint8Arrays work as expected
        return Array.prototype.slice.call(a).map(function (e) {
            var n = Number(e & 0xff).toString(16);
            if (n === 'NaN') {
                throw new Error('invalid input resulted in NaN');
            }

            switch (n.length) {
                case 0: return '00'; // just being careful, shouldn't happen
                case 1: return '0' + n;
                case 2: return n;
                default: throw new Error('unexpected value');
            }
        }).join('');
    };

    Util.deduplicateString = function (array) {
        var a = array.slice();
        for(var i=0; i<a.length; i++) {
            for(var j=i+1; j<a.length; j++) {
                if(a[i] === a[j]) { a.splice(j--, 1); }
            }
        }
        return a;
    };

    /*
     *  Saving files
     */
    Util.fixFileName = function (filename) {
        return filename.replace(/ /g, '-').replace(/[\/\?]/g, '_')
            .replace(/_+/g, '_');
    };

    var oneKilobyte = 1024;
    var oneMegabyte = 1024 * oneKilobyte;
    var oneGigabyte = 1024 * oneMegabyte;

    Util.bytesToGigabytes = function (bytes) {
        return Math.ceil(bytes / oneGigabyte * 100) / 100;
    };

    Util.bytesToMegabytes = function (bytes) {
        return Math.ceil(bytes / oneMegabyte * 100) / 100;
    };

    Util.bytesToKilobytes = function (bytes) {
        return Math.ceil(bytes / oneKilobyte * 100) / 100;
    };

    Util.magnitudeOfBytes = function (bytes) {
        if (bytes >= oneGigabyte) { return 'GB'; }
        else if (bytes >= oneMegabyte) { return 'MB'; }
    };

    Util.fetch = function (src, cb) {
        var done = false;
        var CB = function (err, res) {
            if (done) { return; }
            done = true;
            cb(err, res);
        };

        var xhr = new XMLHttpRequest();
        xhr.open("GET", src, true);
        xhr.responseType = "arraybuffer";
        xhr.onload = function () {
            if (/^4/.test(''+this.status)) {
                return CB('XHR_ERROR');
            }
            return void CB(void 0, new Uint8Array(xhr.response));
        };
        xhr.send(null);
    };

    Util.throttle = function (f, ms) {
        var to;
        var g = function () {
            window.clearTimeout(to);
            to = window.setTimeout(f, ms);
        };
        return g;
    };

    /*  takes a function (f) and a time (t) in ms. returns a function wrapper
        which prevents the internal function from being called more than once
        every t ms. if the function is prevented, returns time til next valid
        execution, else null.
    */
    Util.notAgainForAnother = function (f, t) {
        if (typeof(f) !== 'function' || typeof(t) !== 'number') {
            throw new Error("invalid inputs");
        }
        var last = null;
        return function () {
            var now = +new Date();
            if (last && now <= last + t) { return t - (now - last); }
            last = now;
            f();
            return null;
        };
    };

    Util.createRandomInteger = function () {
        return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
    };

    /* for wrapping async functions such that they can only be called once */
    Util.once = function (f) {
        var called;
        return function () {
            if (called) { return; }
            called = true;
            f.apply(this, Array.prototype.slice.call(arguments));
        };
    };

    Util.slice = function (A) {
        return Array.prototype.slice.call(A);
    };

    Util.blobToImage = function (blob, cb) {
        var reader = new FileReader();
        reader.onloadend = function() {
            cb(reader.result);
        };
        reader.readAsDataURL(blob);
    };
    Util.blobURLToImage = function (url, cb) {
        var xhr = new XMLHttpRequest();
        xhr.onload = function() {
            var reader = new FileReader();
            reader.onloadend = function() {
                cb(reader.result);
            };
            reader.readAsDataURL(xhr.response);
        };
        xhr.open('GET', url);
        xhr.responseType = 'blob';
        xhr.send();
    };

    return Util;
});
<commit_msg>pass 'self' into common-util instead of using window<commit_after>(function (window) {
define([], function () {
    var Util = {};

    // If once is true, after the event has been fired, any further handlers which are
    // registered will fire immediately, and this type of event cannot be fired twice.
    Util.mkEvent = function (once) {
        var handlers = [];
        var fired = false;
        return {
            reg: function (cb) {
                if (once && fired) { return void setTimeout(cb); }
                handlers.push(cb);
            },
            unreg: function (cb) {
                if (handlers.indexOf(cb) === -1) { throw new Error("Not registered"); }
                handlers.splice(handlers.indexOf(cb), 1);
            },
            fire: function () {
                if (once && fired) { return; }
                fired = true;
                var args = Array.prototype.slice.call(arguments);
                handlers.forEach(function (h) { h.apply(null, args); });
            }
        };
    };

    Util.find = function (map, path) {
        var l = path.length;
        for (var i = 0; i < l; i++) {
            if (typeof(map[path[i]]) === 'undefined') { return; }
            map = map[path[i]];
        }
        return map;
    };

    Util.uid = function () {
        return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
            .toString(32).replace(/\./g, '');
    };

    Util.fixHTML = function (str) {
        if (!str) { return ''; }
        return str.replace(/[<>&"']/g, function (x) {
            return ({ "<": "<", ">": "&gt", "&": "&", '"': """, "'": "'" })[x];
        });
    };

    Util.hexToBase64 = function (hex) {
        var hexArray = hex
            .replace(/\r|\n/g, "")
            .replace(/([\da-fA-F]{2}) ?/g, "0x$1 ")
            .replace(/ +$/, "")
            .split(" ");
        var byteString = String.fromCharCode.apply(null, hexArray);
        return window.btoa(byteString).replace(/\//g, '-').replace(/=+$/, '');
    };

    Util.base64ToHex = function (b64String) {
        var hexArray = [];
        atob(b64String.replace(/-/g, '/')).split("").forEach(function(e){
            var h = e.charCodeAt(0).toString(16);
            if (h.length === 1) { h = "0"+h; }
            hexArray.push(h);
        });
        return hexArray.join("");
    };

    Util.uint8ArrayToHex = function (a) {
        // call slice so Uint8Arrays work as expected
        return Array.prototype.slice.call(a).map(function (e) {
            var n = Number(e & 0xff).toString(16);
            if (n === 'NaN') {
                throw new Error('invalid input resulted in NaN');
            }

            switch (n.length) {
                case 0: return '00'; // just being careful, shouldn't happen
                case 1: return '0' + n;
                case 2: return n;
                default: throw new Error('unexpected value');
            }
        }).join('');
    };

    Util.deduplicateString = function (array) {
        var a = array.slice();
        for(var i=0; i<a.length; i++) {
            for(var j=i+1; j<a.length; j++) {
                if(a[i] === a[j]) { a.splice(j--, 1); }
            }
        }
        return a;
    };

    /*
     *  Saving files
     */
    Util.fixFileName = function (filename) {
        return filename.replace(/ /g, '-').replace(/[\/\?]/g, '_')
            .replace(/_+/g, '_');
    };

    var oneKilobyte = 1024;
    var oneMegabyte = 1024 * oneKilobyte;
    var oneGigabyte = 1024 * oneMegabyte;

    Util.bytesToGigabytes = function (bytes) {
        return Math.ceil(bytes / oneGigabyte * 100) / 100;
    };

    Util.bytesToMegabytes = function (bytes) {
        return Math.ceil(bytes / oneMegabyte * 100) / 100;
    };

    Util.bytesToKilobytes = function (bytes) {
        return Math.ceil(bytes / oneKilobyte * 100) / 100;
    };

    Util.magnitudeOfBytes = function (bytes) {
        if (bytes >= oneGigabyte) { return 'GB'; }
        else if (bytes >= oneMegabyte) { return 'MB'; }
    };

    Util.fetch = function (src, cb) {
        var done = false;
        var CB = function (err, res) {
            if (done) { return; }
            done = true;
            cb(err, res);
        };

        var xhr = new XMLHttpRequest();
        xhr.open("GET", src, true);
        xhr.responseType = "arraybuffer";
        xhr.onload = function () {
            if (/^4/.test(''+this.status)) {
                return CB('XHR_ERROR');
            }
            return void CB(void 0, new Uint8Array(xhr.response));
        };
        xhr.send(null);
    };

    Util.throttle = function (f, ms) {
        var to;
        var g = function () {
            window.clearTimeout(to);
            to = window.setTimeout(f, ms);
        };
        return g;
    };

    /*  takes a function (f) and a time (t) in ms. returns a function wrapper
        which prevents the internal function from being called more than once
        every t ms. if the function is prevented, returns time til next valid
        execution, else null.
    */
    Util.notAgainForAnother = function (f, t) {
        if (typeof(f) !== 'function' || typeof(t) !== 'number') {
            throw new Error("invalid inputs");
        }
        var last = null;
        return function () {
            var now = +new Date();
            if (last && now <= last + t) { return t - (now - last); }
            last = now;
            f();
            return null;
        };
    };

    Util.createRandomInteger = function () {
        return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
    };

    /* for wrapping async functions such that they can only be called once */
    Util.once = function (f) {
        var called;
        return function () {
            if (called) { return; }
            called = true;
            f.apply(this, Array.prototype.slice.call(arguments));
        };
    };

    Util.slice = function (A) {
        return Array.prototype.slice.call(A);
    };

    Util.blobToImage = function (blob, cb) {
        var reader = new FileReader();
        reader.onloadend = function() {
            cb(reader.result);
        };
        reader.readAsDataURL(blob);
    };
    Util.blobURLToImage = function (url, cb) {
        var xhr = new XMLHttpRequest();
        xhr.onload = function() {
            var reader = new FileReader();
            reader.onloadend = function() {
                cb(reader.result);
            };
            reader.readAsDataURL(xhr.response);
        };
        xhr.open('GET', url);
        xhr.responseType = 'blob';
        xhr.send();
    };

    return Util;
});
}(self));
<|endoftext|>


<commit_before>const console = require('console'); // Stubbed in tests by proxyquire
const fs = require('fs');
const optimist = require('optimist');
const os = require('os');
const spawnArgs = require('spawn-args');
const spawnSync = require('cross-spawn').sync;

const configUtils = require('./config-utils');
const Dredd = require('./dredd');
const interactiveConfig = require('./interactive-config');
const logger = require('./logger');
const { applyLoggingOptions } = require('./configuration');
const { spawn } = require('./child-process');

const packageData = require('../package.json');

class DreddCommand {
  constructor(options = {}, cb) {
    this.cb = cb;
    this.finished = false;
    ({ exit: this.exit, custom: this.custom } = options);

    this.setExitOrCallback();

    if (!this.custom) { this.custom = {}; }

    if (!this.custom.cwd || typeof this.custom.cwd !== 'string') {
      this.custom.cwd = process.cwd();
    }

    if (!this.custom.argv || !Array.isArray(this.custom.argv)) {
      this.custom.argv = [];
    }
  }

  setOptimistArgv() {
    this.optimist = optimist(this.custom.argv, this.custom.cwd);
    this.cliArgv = this.optimist.argv;

    this.optimist.usage(`\
Usage:
  $ dredd init

Or:
  $ dredd <path or URL to API description document> <URL of tested server> [OPTIONS]

Example:
  $ dredd ./api-description.apib http://127.0.0.1:3000 --dry-run\
`)
      .options(Dredd.options)
      .wrap(80);

    this.argv = this.optimist.argv;
    this.argv = applyLoggingOptions(this.argv);
  }

  // Gracefully terminate server
  stopServer(callback) {
    if (!this.serverProcess || !this.serverProcess.spawned) {
      logger.verbose('No backend server process to terminate.');
      return callback();
    }
    if (this.serverProcess.terminated) {
      logger.debug('The backend server process has already terminated');
      return callback();
    }
    logger.verbose('Terminating backend server process, PID', this.serverProcess.pid);
    this.serverProcess.terminate({ force: true });
    this.serverProcess.on('exit', () => callback());
  }

  // This thing-a-ma-bob here is only for purpose of testing
  // It's basically a dependency injection for the process.exit function
  setExitOrCallback() {
    if (!this.cb) {
      if (this.exit && this.exit === process.exit) {
        this.sigIntEventAdd = true;
      }

      if (this.exit) {
        this._processExit = (exitStatus) => {
          logger.verbose(`Exiting Dredd process with status '${exitStatus}'.`);
          logger.debug('Using configured custom exit() method to terminate the Dredd process.');
          this.finished = true;
          this.stopServer(() => {
            this.exit(exitStatus);
          });
        };
      } else {
        this._processExit = (exitStatus) => {
          logger.verbose(`Exiting Dredd process with status '${exitStatus}'.`);
          logger.debug('Using native process.exit() method to terminate the Dredd process.');
          this.stopServer(() => process.exit(exitStatus));
        };
      }
    } else {
      this._processExit = (exitStatus) => {
        logger.verbose(`Exiting Dredd process with status '${exitStatus}'.`);
        logger.debug('Using configured custom callback to terminate the Dredd process.');
        this.finished = true;
        if (this.sigIntEventAdded) {
          if (this.serverProcess && !this.serverProcess.terminated) {
            logger.verbose('Killing backend server process before Dredd exits.');
            this.serverProcess.signalKill();
          }
          process.removeEventListener('SIGINT', this.commandSigInt);
        }
        this.cb(exitStatus);
        return this;
      };
    }
  }

  moveBlueprintArgToPath() {
    // Transform path and p argument to array if it's not
    if (!Array.isArray(this.argv.path)) {
      this.argv.path = this.argv.p = [this.argv.path];
    }
  }

  checkRequiredArgs() {
    let argError = false;

    // If 'blueprint' is missing
    if (!this.argv._[0]) {
      console.error('\nError: Must specify path to API description document.');
      argError = true;
    }

    // If 'endpoint' is missing
    if (!this.argv._[1]) {
      console.error('\nError: Must specify URL of the tested API instance.');
      argError = true;
    }

    // Show help if argument is missing
    if (argError) {
      console.error('\n');
      this.optimist.showHelp(console.error);
      this._processExit(1);
    }
  }

  runExitingActions() {
    // Run interactive config
    if (this.argv._[0] === 'init' || this.argv.init === true) {
      logger.silly('Starting interactive configuration.');
      this.finished = true;
      interactiveConfig.run(this.argv, (config) => {
        configUtils.save(config);
        console.log('');
        console.log('Configuration saved to dredd.yml');
        console.log('');
        if (config.language === 'nodejs') {
          console.log('Run test now, with:');
        } else {
          console.log('Install hooks handler and run Dredd test with:');
        }
        console.log('');
        if (config.language === 'ruby') {
          console.log('  $ gem install dredd_hooks');
        } else if (config.language === 'python') {
          console.log('  $ pip install dredd_hooks');
        } else if (config.language === 'php') {
          console.log('  $ composer require ddelnano/dredd-hooks-php --dev');
        } else if (config.language === 'perl') {
          console.log('  $ cpanm Dredd::Hooks');
        } else if (config.language === 'go') {
          console.log('  $ go get github.com/snikch/goodman/cmd/goodman');
        } else if (config.language === 'rust') {
          console.log('  $ cargo install dredd-hooks');
        }

        console.log('  $ dredd');
        console.log('');

        this._processExit(0);
      });

    // Show help
    } else if (this.argv.help === true) {
      logger.silly('Printing help.');
      this.optimist.showHelp(console.error);
      this._processExit(0);

    // Show version
    } else if (this.argv.version === true) {
      logger.silly('Printing version.');
      console.log(`\
${packageData.name} v${packageData.version} \
(${os.type()} ${os.release()}; ${os.arch()})\
`);
      this._processExit(0);
    }
  }

  loadDreddFile() {
    const configPath = this.argv.config;
    logger.verbose('Loading configuration file:', configPath);

    if (configPath && fs.existsSync(configPath)) {
      logger.info(`Configuration '${configPath}' found, ignoring other arguments.`);
      this.argv = configUtils.load(configPath);
    }

    // Overwrite saved config with cli arguments
    Object.keys(this.cliArgv).forEach((key) => {
      const value = this.cliArgv[key];
      if (key !== '_' && key !== '$0') {
        this.argv[key] = value;
      }
    });

    this.argv = applyLoggingOptions(this.argv);
  }

  parseCustomConfig() {
    this.argv.custom = configUtils.parseCustom(this.argv.custom);
  }

  runServerAndThenDredd() {
    if (!this.argv.server) {
      logger.verbose('No backend server process specified, starting testing at once');
      this.runDredd(this.dreddInstance);
    } else {
      logger.verbose('Backend server process specified, starting backend server and then testing');

      const parsedArgs = spawnArgs(this.argv.server);
      const command = parsedArgs.shift();

      logger.verbose(`Using '${command}' as a server command, ${JSON.stringify(parsedArgs)} as arguments`);
      this.serverProcess = spawn(command, parsedArgs);
      logger.info(`Starting backend server process with command: ${this.argv.server}`);

      this.serverProcess.stdout.setEncoding('utf8');
      this.serverProcess.stdout.on('data', data => process.stdout.write(data.toString()));

      this.serverProcess.stderr.setEncoding('utf8');
      this.serverProcess.stderr.on('data', data => process.stdout.write(data.toString()));

      this.serverProcess.on('signalTerm', () => logger.verbose('Gracefully terminating the backend server process'));
      this.serverProcess.on('signalKill', () => logger.verbose('Killing the backend server process'));

      this.serverProcess.on('crash', (exitStatus, killed) => {
        if (killed) { logger.info('Backend server process was killed'); }
      });

      this.serverProcess.on('exit', () => {
        logger.info('Backend server process exited');
      });

      this.serverProcess.on('error', (err) => {
        logger.error('Command to start backend server process failed, exiting Dredd', err);
        this._processExit(1);
      });

      // Ensure server is not running when dredd exits prematurely somewhere
      process.on('beforeExit', () => {
        if (this.serverProcess && !this.serverProcess.terminated) {
          logger.verbose('Killing backend server process before Dredd exits');
          this.serverProcess.signalKill();
        }
      });

      // Ensure server is not running when dredd exits prematurely somewhere
      process.on('exit', () => {
        if (this.serverProcess && !this.serverProcess.terminated) {
          logger.verbose('Killing backend server process on Dredd\'s exit');
          this.serverProcess.signalKill();
        }
      });

      const waitSecs = parseInt(this.argv['server-wait'], 10);
      const waitMilis = waitSecs * 1000;
      logger.info(`Waiting ${waitSecs} seconds for backend server process to start`);

      this.wait = setTimeout(() => {
        this.runDredd(this.dreddInstance);
      }
        , waitMilis);
    }
  }

  // This should be handled in a better way in the future:
  // https://github.com/apiaryio/dredd/issues/625
  logDebuggingInfo(config) {
    logger.debug('Dredd version:', packageData.version);
    logger.debug('Node.js version:', process.version);
    logger.debug('Node.js environment:', process.versions);
    logger.debug('System version:', os.type(), os.release(), os.arch());
    try {
      const npmVersion = spawnSync('npm', ['--version']).stdout.toString().trim();
      logger.debug('npm version:', npmVersion || 'unable to determine npm version');
    } catch (err) {
      logger.debug('npm version: unable to determine npm version:', err);
    }
    logger.debug('Configuration:', JSON.stringify(config));
  }

  run() {
    for (const task of [
      this.setOptimistArgv,
      this.parseCustomConfig,
      this.runExitingActions,
      this.loadDreddFile,
      this.checkRequiredArgs,
      this.moveBlueprintArgToPath
    ]) {
      task.call(this);
      if (this.finished) { return; }
    }

    const configurationForDredd = this.initConfig();
    this.logDebuggingInfo(configurationForDredd);
    this.dreddInstance = this.initDredd(configurationForDredd);

    try {
      this.runServerAndThenDredd();
    } catch (e) {
      logger.error(e.message, e.stack);
      this.stopServer(() => {
        this._processExit(2);
      });
    }
  }

  lastArgvIsApiEndpoint() {
    // When API description path is a glob, some shells are automatically expanding globs and concating
    // result as arguments so I'm taking last argument as API endpoint server URL and removing it
    // from optimist's args
    this.server = this.argv._[this.argv._.length - 1];
    this.argv._.splice(this.argv._.length - 1, 1);
    return this;
  }

  takeRestOfParamsAsPath() {
    // And rest of arguments concating to 'path' and 'p' opts, duplicates are filtered out later
    this.argv.p = this.argv.path = this.argv.path.concat(this.argv._);
    return this;
  }

  initConfig() {
    this.lastArgvIsApiEndpoint().takeRestOfParamsAsPath();

    const configuration = {
      server: this.server,
      options: this.argv
    };

    // Push first argument (without some known configuration --key) into paths
    if (!configuration.options.path) { configuration.options.path = []; }
    configuration.options.path.push(this.argv._[0]);

    configuration.custom = this.custom;

    return configuration;
  }

  initDredd(configuration) {
    return new Dredd(configuration);
  }

  commandSigInt() {
    logger.error('\nShutting down from keyboard interruption (Ctrl+C)');
    this.dreddInstance.transactionsComplete(() => this._processExit(0));
  }

  runDredd(dreddInstance) {
    if (this.sigIntEventAdd) {
      // Handle SIGINT from user
      this.sigIntEventAdded = !(this.sigIntEventAdd = false);
      process.on('SIGINT', this.commandSigInt);
    }

    logger.verbose('Running Dredd instance.');
    dreddInstance.run((error, stats) => {
      logger.verbose('Dredd instance run finished.');
      this.exitWithStatus(error, stats);
    });

    return this;
  }

  exitWithStatus(error, stats) {
    if (error) {
      if (error.message) { logger.error(error.message); }
      this._processExit(1);
    }

    if ((stats.failures + stats.errors) > 0) {
      this._processExit(1);
    } else {
      this._processExit(0);
    }
  }
}

module.exports = DreddCommand;
<commit_msg>test: fix flaky test case in cli-test.js<commit_after>const console = require('console'); // Stubbed in tests by proxyquire
const fs = require('fs');
const optimist = require('optimist');
const os = require('os');
const spawnArgs = require('spawn-args');
const spawnSync = require('cross-spawn').sync;

const configUtils = require('./config-utils');
const Dredd = require('./dredd');
const interactiveConfig = require('./interactive-config');
const logger = require('./logger');
const { applyLoggingOptions } = require('./configuration');
const { spawn } = require('./child-process');

const packageData = require('../package.json');

class DreddCommand {
  constructor(options = {}, cb) {
    this.cb = cb;
    this.finished = false;
    ({ exit: this.exit, custom: this.custom } = options);

    this.setExitOrCallback();

    if (!this.custom) { this.custom = {}; }

    if (!this.custom.cwd || typeof this.custom.cwd !== 'string') {
      this.custom.cwd = process.cwd();
    }

    if (!this.custom.argv || !Array.isArray(this.custom.argv)) {
      this.custom.argv = [];
    }
  }

  setOptimistArgv() {
    this.optimist = optimist(this.custom.argv, this.custom.cwd);
    this.cliArgv = this.optimist.argv;

    this.optimist.usage(`\
Usage:
  $ dredd init

Or:
  $ dredd <path or URL to API description document> <URL of tested server> [OPTIONS]

Example:
  $ dredd ./api-description.apib http://127.0.0.1:3000 --dry-run\
`)
      .options(Dredd.options)
      .wrap(80);

    this.argv = this.optimist.argv;
    this.argv = applyLoggingOptions(this.argv);
  }

  // Gracefully terminate server
  stopServer(callback) {
    if (!this.serverProcess || !this.serverProcess.spawned) {
      logger.verbose('No backend server process to terminate.');
      return callback();
    }
    if (this.serverProcess.terminated) {
      logger.debug('The backend server process has already terminated');
      return callback();
    }
    logger.verbose('Terminating backend server process, PID', this.serverProcess.pid);
    this.serverProcess.terminate({ force: true });
    this.serverProcess.on('exit', () => callback());
  }

  // This thing-a-ma-bob here is only for purpose of testing
  // It's basically a dependency injection for the process.exit function
  setExitOrCallback() {
    if (!this.cb) {
      if (this.exit && this.exit === process.exit) {
        this.sigIntEventAdd = true;
      }

      if (this.exit) {
        this._processExit = (exitStatus) => {
          logger.verbose(`Exiting Dredd process with status '${exitStatus}'.`);
          logger.debug('Using configured custom exit() method to terminate the Dredd process.');
          this.finished = true;
          this.stopServer(() => {
            this.exit(exitStatus);
          });
        };
      } else {
        this._processExit = (exitStatus) => {
          logger.verbose(`Exiting Dredd process with status '${exitStatus}'.`);
          logger.debug('Using native process.exit() method to terminate the Dredd process.');
          this.stopServer(() => process.exit(exitStatus));
        };
      }
    } else {
      this._processExit = (exitStatus) => {
        logger.verbose(`Exiting Dredd process with status '${exitStatus}'.`);
        logger.debug('Using configured custom callback to terminate the Dredd process.');
        this.finished = true;
        if (this.sigIntEventAdded) {
          if (this.serverProcess && !this.serverProcess.terminated) {
            logger.verbose('Killing backend server process before Dredd exits.');
            this.serverProcess.signalKill();
          }
          process.removeEventListener('SIGINT', this.commandSigInt);
        }
        this.cb(exitStatus);
        return this;
      };
    }
  }

  moveBlueprintArgToPath() {
    // Transform path and p argument to array if it's not
    if (!Array.isArray(this.argv.path)) {
      this.argv.path = this.argv.p = [this.argv.path];
    }
  }

  checkRequiredArgs() {
    let argError = false;

    // If 'blueprint' is missing
    if (!this.argv._[0]) {
      console.error('\nError: Must specify path to API description document.');
      argError = true;
    }

    // If 'endpoint' is missing
    if (!this.argv._[1]) {
      console.error('\nError: Must specify URL of the tested API instance.');
      argError = true;
    }

    // Show help if argument is missing
    if (argError) {
      console.error('\n');
      this.optimist.showHelp(console.error);
      this._processExit(1);
    }
  }

  runExitingActions() {
    // Run interactive config
    if (this.argv._[0] === 'init' || this.argv.init === true) {
      logger.silly('Starting interactive configuration.');
      this.finished = true;
      interactiveConfig.run(this.argv, (config) => {
        configUtils.save(config);
        console.log('');
        console.log('Configuration saved to dredd.yml');
        console.log('');
        if (config.language === 'nodejs') {
          console.log('Run test now, with:');
        } else {
          console.log('Install hooks handler and run Dredd test with:');
        }
        console.log('');
        if (config.language === 'ruby') {
          console.log('  $ gem install dredd_hooks');
        } else if (config.language === 'python') {
          console.log('  $ pip install dredd_hooks');
        } else if (config.language === 'php') {
          console.log('  $ composer require ddelnano/dredd-hooks-php --dev');
        } else if (config.language === 'perl') {
          console.log('  $ cpanm Dredd::Hooks');
        } else if (config.language === 'go') {
          console.log('  $ go get github.com/snikch/goodman/cmd/goodman');
        } else if (config.language === 'rust') {
          console.log('  $ cargo install dredd-hooks');
        }

        console.log('  $ dredd');
        console.log('');

        this._processExit(0);
      });

    // Show help
    } else if (this.argv.help === true) {
      logger.silly('Printing help.');
      this.optimist.showHelp(console.error);
      this._processExit(0);

    // Show version
    } else if (this.argv.version === true) {
      logger.silly('Printing version.');
      console.log(`\
${packageData.name} v${packageData.version} \
(${os.type()} ${os.release()}; ${os.arch()})\
`);
      this._processExit(0);
    }
  }

  loadDreddFile() {
    const configPath = this.argv.config;
    logger.verbose('Loading configuration file:', configPath);

    if (configPath && fs.existsSync(configPath)) {
      logger.info(`Configuration '${configPath}' found, ignoring other arguments.`);
      this.argv = configUtils.load(configPath);
    }

    // Overwrite saved config with cli arguments
    Object.keys(this.cliArgv).forEach((key) => {
      const value = this.cliArgv[key];
      if (key !== '_' && key !== '$0') {
        this.argv[key] = value;
      }
    });

    this.argv = applyLoggingOptions(this.argv);
  }

  parseCustomConfig() {
    this.argv.custom = configUtils.parseCustom(this.argv.custom);
  }

  runServerAndThenDredd() {
    if (!this.argv.server) {
      logger.verbose('No backend server process specified, starting testing at once');
      this.runDredd(this.dreddInstance);
    } else {
      logger.verbose('Backend server process specified, starting backend server and then testing');

      const parsedArgs = spawnArgs(this.argv.server);
      const command = parsedArgs.shift();

      logger.verbose(`Using '${command}' as a server command, ${JSON.stringify(parsedArgs)} as arguments`);
      this.serverProcess = spawn(command, parsedArgs);
      logger.info(`Starting backend server process with command: ${this.argv.server}`);

      this.serverProcess.stdout.setEncoding('utf8');
      this.serverProcess.stdout.on('data', data => process.stdout.write(data.toString()));

      this.serverProcess.stderr.setEncoding('utf8');
      this.serverProcess.stderr.on('data', data => process.stdout.write(data.toString()));

      this.serverProcess.on('signalTerm', () => logger.verbose('Gracefully terminating the backend server process'));
      this.serverProcess.on('signalKill', () => logger.verbose('Killing the backend server process'));

      this.serverProcess.on('crash', (exitStatus, killed) => {
        if (killed) { logger.info('Backend server process was killed'); }
      });

      this.serverProcess.on('exit', () => {
        logger.info('Backend server process exited');
      });

      this.serverProcess.on('error', (err) => {
        logger.error('Command to start backend server process failed, exiting Dredd', err);
        this._processExit(1);
      });

      // Fixes https://github.com/apiaryio/dredd/issues/1030
      process.stdin.on('error', (err) => {
        logger.error(`Error in stdin: ${err}`);
      });

      // Ensure server is not running when dredd exits prematurely somewhere
      process.on('beforeExit', () => {
        if (this.serverProcess && !this.serverProcess.terminated) {
          logger.verbose('Killing backend server process before Dredd exits');
          this.serverProcess.signalKill();
        }
      });

      // Ensure server is not running when dredd exits prematurely somewhere
      process.on('exit', () => {
        if (this.serverProcess && !this.serverProcess.terminated) {
          logger.verbose('Killing backend server process on Dredd\'s exit');
          this.serverProcess.signalKill();
        }
      });

      const waitSecs = parseInt(this.argv['server-wait'], 10);
      const waitMilis = waitSecs * 1000;
      logger.info(`Waiting ${waitSecs} seconds for backend server process to start`);

      this.wait = setTimeout(() => {
        this.runDredd(this.dreddInstance);
      }
        , waitMilis);
    }
  }

  // This should be handled in a better way in the future:
  // https://github.com/apiaryio/dredd/issues/625
  logDebuggingInfo(config) {
    logger.debug('Dredd version:', packageData.version);
    logger.debug('Node.js version:', process.version);
    logger.debug('Node.js environment:', process.versions);
    logger.debug('System version:', os.type(), os.release(), os.arch());
    try {
      const npmVersion = spawnSync('npm', ['--version']).stdout.toString().trim();
      logger.debug('npm version:', npmVersion || 'unable to determine npm version');
    } catch (err) {
      logger.debug('npm version: unable to determine npm version:', err);
    }
    logger.debug('Configuration:', JSON.stringify(config));
  }

  run() {
    for (const task of [
      this.setOptimistArgv,
      this.parseCustomConfig,
      this.runExitingActions,
      this.loadDreddFile,
      this.checkRequiredArgs,
      this.moveBlueprintArgToPath
    ]) {
      task.call(this);
      if (this.finished) { return; }
    }

    const configurationForDredd = this.initConfig();
    this.logDebuggingInfo(configurationForDredd);
    this.dreddInstance = this.initDredd(configurationForDredd);

    try {
      this.runServerAndThenDredd();
    } catch (e) {
      logger.error(e.message, e.stack);
      this.stopServer(() => {
        this._processExit(2);
      });
    }
  }

  lastArgvIsApiEndpoint() {
    // When API description path is a glob, some shells are automatically expanding globs and concating
    // result as arguments so I'm taking last argument as API endpoint server URL and removing it
    // from optimist's args
    this.server = this.argv._[this.argv._.length - 1];
    this.argv._.splice(this.argv._.length - 1, 1);
    return this;
  }

  takeRestOfParamsAsPath() {
    // And rest of arguments concating to 'path' and 'p' opts, duplicates are filtered out later
    this.argv.p = this.argv.path = this.argv.path.concat(this.argv._);
    return this;
  }

  initConfig() {
    this.lastArgvIsApiEndpoint().takeRestOfParamsAsPath();

    const configuration = {
      server: this.server,
      options: this.argv
    };

    // Push first argument (without some known configuration --key) into paths
    if (!configuration.options.path) { configuration.options.path = []; }
    configuration.options.path.push(this.argv._[0]);

    configuration.custom = this.custom;

    return configuration;
  }

  initDredd(configuration) {
    return new Dredd(configuration);
  }

  commandSigInt() {
    logger.error('\nShutting down from keyboard interruption (Ctrl+C)');
    this.dreddInstance.transactionsComplete(() => this._processExit(0));
  }

  runDredd(dreddInstance) {
    if (this.sigIntEventAdd) {
      // Handle SIGINT from user
      this.sigIntEventAdded = !(this.sigIntEventAdd = false);
      process.on('SIGINT', this.commandSigInt);
    }

    logger.verbose('Running Dredd instance.');
    dreddInstance.run((error, stats) => {
      logger.verbose('Dredd instance run finished.');
      this.exitWithStatus(error, stats);
    });

    return this;
  }

  exitWithStatus(error, stats) {
    if (error) {
      if (error.message) { logger.error(error.message); }
      this._processExit(1);
    }

    if ((stats.failures + stats.errors) > 0) {
      this._processExit(1);
    } else {
      this._processExit(0);
    }
  }
}

module.exports = DreddCommand;
<|endoftext|>


<commit_before>//Your APE server URL
APE.server = 'ape2.crusthq.com';
<commit_msg>discarded file<commit_after><|endoftext|>


<commit_before>var Editor = {
    init: function (apiKey) {
        extend(Remix, {
            onError: this._onError,
            onPlayerProgress: this._onPlayerProgress
        });
        Remix.init(apiKey);

        this._snips = {};

        this._remixJsElt = document.getElementById('remixJs');
        this._progressElt = document.getElementById('progress');
    },

    _onError: function(message) {
        alert(message);
    },

    _onPlayerProgress: function(progress, sourceIndex, sourcePosition) {
        if (sourceIndex != this._sourceIndex) {
            Remix.log(sourceIndex);
            this._sourceIndex = sourceIndex;
        }
        Editor._progressElt.style.width = 100 * progress + '%';
    },

    getScript: function () {
        return this._remixJsElt.value;
    },

    run: function() {
        var script = this.getScript();
        if (!script) {
            return;
        }
        var remixCalled = false;
        var play = function () {
            Remix.remix.apply(Remix, arguments);
            remixCalled = true;
        };
        // TODO use copies
        var tracks = Remix._tracks;
        var track = this.selectedTrack || tracks[0];
        if (!track) {
            Remix.onError('No tracks available.');
            return;
        }
        var analysis = track.analysis;
        var snips = this._snips;
        try {
            eval(script);
            if (!remixCalled) {
                Remix.onError('Call the play() function to play your remix');
            }
        }
        catch(e) {
            Remix.onError(e);
        }
    }
};
<commit_msg>add selection to script scope<commit_after>var Editor = {
    init: function (apiKey) {
        extend(Remix, {
            onError: this._onError,
            onPlayerProgress: this._onPlayerProgress
        });
        Remix.init(apiKey);

        this._snips = {};

        this._remixJsElt = document.getElementById('remixJs');
        this._progressElt = document.getElementById('progress');
    },

    _onError: function(message) {
        alert(message);
    },

    _onPlayerProgress: function(progress, sourceIndex, sourcePosition) {
        if (sourceIndex != this._sourceIndex) {
            Remix.log(sourceIndex);
            this._sourceIndex = sourceIndex;
        }
        Editor._progressElt.style.width = 100 * progress + '%';
    },

    getScript: function () {
        return this._remixJsElt.value;
    },

    run: function() {
        var script = this.getScript();
        if (!script) {
            return;
        }
        var remixCalled = false;
        var play = function () {
            Remix.remix.apply(Remix, arguments);
            remixCalled = true;
        };
        // TODO use copies
        var tracks = Remix._tracks;
        var track = this.selectedTrack || tracks[0];
        if (!track) {
            Remix.onError('No tracks available.');
            return;
        }
        var selection = track.selection;
        var analysis = track.analysis;
        var snips = this._snips;
        try {
            eval(script);
            if (!remixCalled) {
                Remix.onError('Call the play() function to play your remix');
            }
        }
        catch(e) {
            Remix.onError(e);
        }
    }
};
<|endoftext|>