<commit_before>/**
 * jQuery List - http://github.com/nathggns/jq-list
 *
 * @author Nathaniel Higgins [@nathggns]
 * @author James Hall [@MrRio]
 */
(function (window, document, $, undefined) {

    var global_information = {};
    var default_row = {};
    var defaults = {
        rows: [],
        element: null,
        template: function () {},
        template_html: '',
        children: [],
        events: {},
        count: 1,
        templates: {},
        original: {
            html: '',
            attrs: {},
            data: {}
        }
    };
    var compiled_templates = {};

    /**
     * This function generates a random number, if this number has been generated before
     * it will generate another.
     * @param  {int} length The 'length' is the number of digits in the number, defaults to 10
     * @return {int} Random number
     */
    var getID = (function (length) {
        var IDs = [undefined];

        return function () {
            for (
                var id;
                IDs.indexOf(id) > -1;
                id = Math.floor(
                    Math.random() * Math.pow(10, length || 10)
                )
            );

            return id;
        };
    })();

    /**
     * Get on object to run methods from
     * @param  {object} element Element to run the methods on
     * @return {object} Allows you to run methods
     */
    var getMethods = function (element) {

        var roles = {

            /**
             * Handle clicking the add button, add a list item.
             * @param  {event} e the event variable from the event.
             * @param  {int} index The index 1-based index of the list item that was clicked
             */
            'add': function (e, index) {
                this.add(index - 1);
                this.render();
            },

            /**
             * Handle clicking the delelete button, deletes a list item
             * @param  {event} e the event variable from the event.
             * @param  {int} index The index 1-based index of the list item that was clicked
             */
            'delete': function (e, index) {
                this['delete'](index - 1);
                this.render();
            }
        };
        var methods = {

            /**
             * Initiate the plugin.
             * @param  {string|object} selector The element to run the plugin on
             * @param  {object} options  Options for the plugin. Defaults to {}
             */
            'init': function (selector, options) {
                if (!options) options = {};

                if (!element.data('ran')) {

                    var original = {
                        html: element.html(),
                        attrs: $.extend(true, {}, this.attrs(element.get(0))),
                        data: $.extend(true, {}, element.data())
                    };

                    if (options.events) {
                        $.each(options.events, function (index, events) {
                            if (typeof events === 'function') {
                                options.events[index] = [events];
                            }
                        });
                    }

                    var info = $.extend(true, {}, defaults, {
                        element: element,
                        original: original
                    }, options || {});

                    this.saveInformation(info);

                    if (info.children) {

                        var children = [];

                        $.each(info.children, function (name, child) {
                            if (typeof child === 'string') {
                                child = {
                                    selector: child
                                };
                            }

                            if (!child.name) child.name = name;

                            if (!child.options) child.options = {};

                            child.element = $(child.selector);

                            children.push(child);
                        });

                        info.children = children;
                    }

                    $.each(info.templates, function (name, template) {
                        compiled_templates[name] = run.compileTemplate(template);
                    });

                    this.setTemplate($(selector));

                    for (var i = 0, l = info.count; i < info.count; i++) {
                        this.add(i);
                    }
                }

                this.render();
                element.data('ran', true);
                this.events('init');
            },

            /**
             * Generate a function to replace template placeholders
             * @param  {string|object} template The subtemplate
             * @return {function}
             */
            'compileTemplate': function (template) {
                template = this.giveAttrs(template);

                /**
                 * Replace the $element with the subtemplate
                 * @param  {object} $element Subtemplate placeholder
                 */
                return function ($element) {
                    template = run.runOnString(template, function ($ele) {
                        var $content = $ele.find('[data-content]');

                        if ($content.length > 0) {
                            var html = $element.html();

                            $content.after(html);
                            $content.remove();
                        }
                    });

                    $element.after(template);
                    $element.remove();
                };
            },

            /**
             * Run a callback on a string as if it were an element
             * @param  {string|object}   string   The string to run the callback on
             * @param  {function} callback  the callback to run
             * @return {string]}            The result of the callback
             */
            'runOnString': function (string, callback) {
                if (typeof string !== 'string') string = string.html();
                var $ele = $('<div/>').html(string);

                callback($ele);

                return $ele.html();
            },

            /**
             * Give the attributes to the elements we need to hook into
             * @param  {object|string} $element The element to find children in
             * @return {object|string}          The element after the attributes have been added
             */
            'giveAttrs': function ($element) {
                var return_text = false;

                var key = this.getKey();

                if (typeof $element === 'string') {
                    $element = $('<div/>').html($element);
                    return_text = true;
                }

                var $specials = $element.find('[value], input, button, textarea, select').each(function (i) {
                    $(this).attr('data-identifier', '{{ item.identifier }}-' + i);
                });

                $specials = $specials.add($element.find('[data-role]')).each(function () {
                    $(this).attr('data-parent', key);
                });

                $specials.add($element.find('[data-sublist]')).each(function () {
                    $(this).attr('data-index', '{{ loop.index }}');
                });

                if (return_text) {
                    $element = $element.html();
                }

                return $element;
            },

            /**
             * Add another item to list
             * @param  {int} index The index to add the item after
             */
            'add': function (index) {
                this.events('before_add');

                var info = this.getInformation();
                var row = jQuery.extend(true, {}, default_row);

                row.identifier = this.getKey() + '-' + getID();
                info.rows.splice(index + 1, 0, row);

                this.saveInformation(info);
                this.events('add', index);
            },

            /**
             * Remove an item from the list
             * @param  {int} index The index of the item to remove
             */
            'delete': function (index) {
                this.events('before_delete', index);

                var info = this.getInformation();
                info.rows.splice(index, 1);

                this.saveInformation(info);
                this.events('delete', index);
            },

            /**
             * Render the list
             * @param  {object} vars List of variables to pass to the swig template
             */
            'render': function (vars) {

                this.events('before_render');

                var info = this.getInformation();
                var children = info.children;

                var rows = {};

                var data = {};

                $.each(children, function (index, child) {
                    element.find('[data-sublist="' + child.name + '"]').each(function (index) {
                        rows[child.name + '-' + index] = $(this).list('getInformation').rows;
                    });
                });

                element.find('[data-identifier]').each(function () {
                    data[$(this).data('identifier')] = this;
                });

                var real_rows = this.getInformation().rows;

                element.html(info.template($.extend(true, {}, {
                    list: real_rows,
                    length: real_rows.length,
                    showdelete: real_rows.length > 1
                }, vars || {}, info.render_options || {})));

                this.events('render_before_fix');

                element.find('[data-role]').on('click', function (e) {

                    var $this = $(this);
                    var role = $this.data('role');

                    if ($this.data('has-assigned')) return;

                    $this.data('has-assigned', true);

                    if (roles[role]) {
                        e.preventDefault();
                        roles[role].call(run, e, $this.data('index'));
                    }
                });

                $.each(children, function (index, child) {

                    element.find('[data-sublist="' + child.name + '"]').each(function (index) {

                        var row_name = child.name + '-' + index;

                        if (!rows[row_name]) rows[row_name] = [];

                        var $this = $(this);

                        $this.list(child.element, child.options);

                        var info = $this.list('getInformation');
                        info.render_options = {
                            owner: {
                                index: $this.data('index')
                            }
                        };

                        $this.list('saveInformation', info).list('render');

                        if (rows[row_name].length > 0) {
                            info = $this.list('getInformation');
                            info.rows = rows[row_name];
                            $this.list('saveInformation', info).list('render');
                        }

                    });
                });

                $.each(data, function (identifier, ele) {
                    var $new = element.find('[data-identifier="' + identifier + '"]');

                    var $ele = $(ele);

                    if ($new.length > 0) {

                        $.each(run.attrs($new[0]), function (name, attr) {
                            if (name !== 'type') {
                                $ele.attr(name, attr);
                            }
                        });

                        $ele.insertAfter($new);
                        $new.remove();

                    }

                });

                this.events('render');

            },

            /**
             * Get the stored information about the current plugin instance.
             * @return {object} The info
             */
            'getInformation': function () {
                return global_information[this.getKey()];
            },

            /**
             * Get a key to identify the current instance
             * @return {int} The instance
             */
            'getKey': function () {
                if (!element.data('info-key')) {
                    element.data('info-key', getID());
                }

                return element.data('info-key');
            },

            /**
             * Save information about the current instance
             * @param  {object} info Information to save
             */
            'saveInformation': function (info) {
                this.events('save_info', info);
                global_information[this.getKey()] = info;
            },

            /**
             * Get key-value object of the attributes of a DOMElement
             * @param  {DOMElement} ele The element to fetch attributes from
             * @return {object}     key-value object of attributes
             */
            'attrs': function (ele) {
                ele = ele || element;
                var attrs = {};

                $.each(ele.attributes, function () {
                    attrs[this.nodeName] = this.nodeValue;
                });

                return attrs;
            },

            /**
             * Run event callbacks
             * @param {string} event_name The name of the events to run
             */
            'events': function () {
                if (arguments.length > 0) {
                    var event_name = arguments[0];
                    var info = this.getInformation();
                    var args = Array.prototype.slice.call(arguments, 1);

                    if (info && info.events[event_name]) {
                        $.each(info.events[event_name], function () {
                            this.apply(element, args);
                        });
                    }
                }
            },

            /**
             * Assign an event handler
             * @param  {string} event   the event type
             * @param  {function} handler the handler for the event
             */
            'on': function (event, handler) {
                var info = this.getInformation();

                if (!info.events[event]) info.events[event] = [];

                info.events[event].push(handler);

                this.saveInformation(info);
            },

            /**
             * Remove an event handler
             * @param  {string} event   the event type
             * @param  {function} handler the handler for the event
             */
            'off': function (event, handler) {
                var info = this.getInformation();
                if (info.events[event]) {
                    var index = info.events[event].indexOf(handler);

                    if (index > -1) {
                        info.events[event].splice(index, 1);
                    }
                }

                this.saveInformation(info);
            },

            /**
             * Grab the method runner
             * @return {object} Method runner.
             */
            'methodRunner': function() {
                return run;
            },

            /**
             * Completely reset this instance of the list plugin
             */
            'reset': function() {
                // Get our info and key, as we can't get it later
                var key = this.getKey();
                var info = this.getInformation();

                // Remove the entry in our registry
                delete global_information[key];

                // Remove all data first
                $.each(element.data(), function(name) {
                    element.removeData(name);
                });

                // Re-assign original data
                $.each(info.original.data, function(name, val) {
                    element.data(name, val);
                });

                // Remove all attributes first
                $.each(this.attrs(element[0]), function(name) {
                    if (name !== 'type') {
                        element.removeAttr(name);
                    }
                });

                // Re-assign original attributes
                $.each(info.original.attrs, function(name, val) {
                    if (name !== 'type') {
                        element.attr(name, val);
                    }
                });

                // Reset the html
                element.html(info.original.html);
            },

            /**
             * Get the template
             *
             * @param {bool} return_func Return the template function.
             * @return {string|function} template string or function, depending on return_func
             */
            'getTemplate': function(return_func) {
                var info = this.getInformation();

                return typeof return_func === 'undefined' ? info.template_html : info.template;
            },

            /**
             * Set a template, pass either an element or a string...
             *
             * @param {string|object} Template to set
             */
            'setTemplate': function(string) {
                if (typeof string !== 'string') {
                    string = string.html();
                }

                string = this.giveAttrs(string);

                var $copy = $('<div/>').html(string);
                var $elements;

                var handler = function($elements) {
                    $elements.each(function () {
                        var $this = $(this);
                        var name = $this.data('template');
                        var template = compiled_templates[name];

                        template($this);
                    });
                };

                while (($elements = $copy.find('[data-template]')).length > 0) {
                    handler($elements);
                }

                string = $copy.html();

                var info = this.getInformation();
                info.template_html = string;
                info.template = swig.compile(string);
                this.saveInformation(info);
            }
        };

        var run = function () {
            var name = Array.prototype.shift.call(arguments);

            return methods[name].apply(run, arguments);
        };

        $.each(methods, function (name) {

            run[name] = function () {

                Array.prototype.unshift.call(arguments, name);
                var args = arguments;

                return run.apply(run, args);
            };
        });

        return run;
    };

    /**
     * Get the method from the arguments variable
     *
     * Usage: getMethod.apply(this, arguments);
     */
    var getMethod = function() {
        if (arguments.length < 1) {
            Array.prototype.push.call(arguments, 'init');
        }

        var method = Array.prototype.splice.call(arguments, 0, 1)[0];

        return [method, arguments];
    };

    $.list = getMethods(false);

    $.fn.list = function () {

        var arr = getMethod.apply(this, arguments);
        var method = arr[0];
        var args = arr[1];

        if (!this.data('ran')) {

            var selector;

            if (method !== 'init') {
                Array.prototype.unshift.call(args, method);
                method = 'init';
            }

            if (args.length < 1) {
                selector = '[type="text/swig"]';
            } else {
                selector = args[0];
                Array.prototype.splice.call(args, 0, 1);
            }

            Array.prototype.unshift.call(args, selector);
        }

        var result = getMethods(this)[method].apply(this, args);

        if (result) {
            return result;
        } else {
            return this;
        }
    };

})(window, document, jQuery);<commit_msg>Make it strict.<commit_after>/**
 * jQuery List - http://github.com/nathggns/jq-list
 *
 * @author Nathaniel Higgins [@nathggns]
 * @author James Hall [@MrRio]
 */
(function (window, document, $, swig, undefined) {

    'use strict';

    var global_information = {};
    var default_row = {};
    var defaults = {
        rows: [],
        element: null,
        template: function () {},
        template_html: '',
        children: [],
        events: {},
        count: 1,
        templates: {},
        original: {
            html: '',
            attrs: {},
            data: {}
        }
    };
    var compiled_templates = {};

    /**
     * This function generates a random number, if this number has been generated before
     * it will generate another.
     * @param  {int} length The 'length' is the number of digits in the number, defaults to 10
     * @return {int} Random number
     */
    var getID = (function (length) {
        var IDs = [undefined];

        return function () {
            for (
                var id;
                IDs.indexOf(id) > -1;
                id = Math.floor(
                    Math.random() * Math.pow(10, length || 10)
                )
            );

            return id;
        };
    })();

    /**
     * Get on object to run methods from
     * @param  {object} element Element to run the methods on
     * @return {object} Allows you to run methods
     */
    var getMethods = function (element) {

        var roles = {

            /**
             * Handle clicking the add button, add a list item.
             * @param  {event} e the event variable from the event.
             * @param  {int} index The index 1-based index of the list item that was clicked
             */
            'add': function (e, index) {
                this.add(index - 1);
                this.render();
            },

            /**
             * Handle clicking the delelete button, deletes a list item
             * @param  {event} e the event variable from the event.
             * @param  {int} index The index 1-based index of the list item that was clicked
             */
            'delete': function (e, index) {
                this['delete'](index - 1);
                this.render();
            }
        };
        var methods = {

            /**
             * Initiate the plugin.
             * @param  {string|object} selector The element to run the plugin on
             * @param  {object} options  Options for the plugin. Defaults to {}
             */
            'init': function (selector, options) {
                if (!options) options = {};

                if (!element.data('ran')) {

                    var original = {
                        html: element.html(),
                        attrs: $.extend(true, {}, this.attrs(element.get(0))),
                        data: $.extend(true, {}, element.data())
                    };

                    if (options.events) {
                        $.each(options.events, function (index, events) {
                            if (typeof events === 'function') {
                                options.events[index] = [events];
                            }
                        });
                    }

                    var info = $.extend(true, {}, defaults, {
                        element: element,
                        original: original
                    }, options || {});

                    this.saveInformation(info);

                    if (info.children) {

                        var children = [];

                        $.each(info.children, function (name, child) {
                            if (typeof child === 'string') {
                                child = {
                                    selector: child
                                };
                            }

                            if (!child.name) child.name = name;

                            if (!child.options) child.options = {};

                            child.element = $(child.selector);

                            children.push(child);
                        });

                        info.children = children;
                    }

                    $.each(info.templates, function (name, template) {
                        compiled_templates[name] = run.compileTemplate(template);
                    });

                    this.setTemplate($(selector));

                    for (var i = 0, l = info.count; i < info.count; i++) {
                        this.add(i);
                    }
                }

                this.render();
                element.data('ran', true);
                this.events('init');
            },

            /**
             * Generate a function to replace template placeholders
             * @param  {string|object} template The subtemplate
             * @return {function}
             */
            'compileTemplate': function (template) {
                template = this.giveAttrs(template);

                /**
                 * Replace the $element with the subtemplate
                 * @param  {object} $element Subtemplate placeholder
                 */
                return function ($element) {
                    template = run.runOnString(template, function ($ele) {
                        var $content = $ele.find('[data-content]');

                        if ($content.length > 0) {
                            var html = $element.html();

                            $content.after(html);
                            $content.remove();
                        }
                    });

                    $element.after(template);
                    $element.remove();
                };
            },

            /**
             * Run a callback on a string as if it were an element
             * @param  {string|object}   string   The string to run the callback on
             * @param  {function} callback  the callback to run
             * @return {string]}            The result of the callback
             */
            'runOnString': function (string, callback) {
                if (typeof string !== 'string') string = string.html();
                var $ele = $('<div/>').html(string);

                callback($ele);

                return $ele.html();
            },

            /**
             * Give the attributes to the elements we need to hook into
             * @param  {object|string} $element The element to find children in
             * @return {object|string}          The element after the attributes have been added
             */
            'giveAttrs': function ($element) {
                var return_text = false;

                var key = this.getKey();

                if (typeof $element === 'string') {
                    $element = $('<div/>').html($element);
                    return_text = true;
                }

                var $specials = $element.find('[value], input, button, textarea, select').each(function (i) {
                    $(this).attr('data-identifier', '{{ item.identifier }}-' + i);
                });

                $specials = $specials.add($element.find('[data-role]')).each(function () {
                    $(this).attr('data-parent', key);
                });

                $specials.add($element.find('[data-sublist]')).each(function () {
                    $(this).attr('data-index', '{{ loop.index }}');
                });

                if (return_text) {
                    $element = $element.html();
                }

                return $element;
            },

            /**
             * Add another item to list
             * @param  {int} index The index to add the item after
             */
            'add': function (index) {
                this.events('before_add');

                var info = this.getInformation();
                var row = $.extend(true, {}, default_row);

                row.identifier = this.getKey() + '-' + getID();
                info.rows.splice(index + 1, 0, row);

                this.saveInformation(info);
                this.events('add', index);
            },

            /**
             * Remove an item from the list
             * @param  {int} index The index of the item to remove
             */
            'delete': function (index) {
                this.events('before_delete', index);

                var info = this.getInformation();
                info.rows.splice(index, 1);

                this.saveInformation(info);
                this.events('delete', index);
            },

            /**
             * Render the list
             * @param  {object} vars List of variables to pass to the swig template
             */
            'render': function (vars) {

                this.events('before_render');

                var info = this.getInformation();
                var children = info.children;

                var rows = {};

                var data = {};

                $.each(children, function (index, child) {
                    element.find('[data-sublist="' + child.name + '"]').each(function (index) {
                        rows[child.name + '-' + index] = $(this).list('getInformation').rows;
                    });
                });

                element.find('[data-identifier]').each(function () {
                    data[$(this).data('identifier')] = this;
                });

                var real_rows = this.getInformation().rows;

                element.html(info.template($.extend(true, {}, {
                    list: real_rows,
                    length: real_rows.length,
                    showdelete: real_rows.length > 1
                }, vars || {}, info.render_options || {})));

                this.events('render_before_fix');

                element.find('[data-role]').on('click', function (e) {

                    var $this = $(this);
                    var role = $this.data('role');

                    if ($this.data('has-assigned')) return;

                    $this.data('has-assigned', true);

                    if (roles[role]) {
                        e.preventDefault();
                        roles[role].call(run, e, $this.data('index'));
                    }
                });

                $.each(children, function (index, child) {

                    element.find('[data-sublist="' + child.name + '"]').each(function (index) {

                        var row_name = child.name + '-' + index;

                        if (!rows[row_name]) rows[row_name] = [];

                        var $this = $(this);

                        $this.list(child.element, child.options);

                        var info = $this.list('getInformation');
                        info.render_options = {
                            owner: {
                                index: $this.data('index')
                            }
                        };

                        $this.list('saveInformation', info).list('render');

                        if (rows[row_name].length > 0) {
                            info = $this.list('getInformation');
                            info.rows = rows[row_name];
                            $this.list('saveInformation', info).list('render');
                        }

                    });
                });

                $.each(data, function (identifier, ele) {
                    var $new = element.find('[data-identifier="' + identifier + '"]');

                    var $ele = $(ele);

                    if ($new.length > 0) {

                        $.each(run.attrs($new[0]), function (name, attr) {
                            if (name !== 'type') {
                                $ele.attr(name, attr);
                            }
                        });

                        $ele.insertAfter($new);
                        $new.remove();

                    }

                });

                this.events('render');

            },

            /**
             * Get the stored information about the current plugin instance.
             * @return {object} The info
             */
            'getInformation': function () {
                return global_information[this.getKey()];
            },

            /**
             * Get a key to identify the current instance
             * @return {int} The instance
             */
            'getKey': function () {
                if (!element.data('info-key')) {
                    element.data('info-key', getID());
                }

                return element.data('info-key');
            },

            /**
             * Save information about the current instance
             * @param  {object} info Information to save
             */
            'saveInformation': function (info) {
                this.events('save_info', info);
                global_information[this.getKey()] = info;
            },

            /**
             * Get key-value object of the attributes of a DOMElement
             * @param  {DOMElement} ele The element to fetch attributes from
             * @return {object}     key-value object of attributes
             */
            'attrs': function (ele) {
                ele = ele || element;
                var attrs = {};

                $.each(ele.attributes, function () {
                    attrs[this.nodeName] = this.nodeValue;
                });

                return attrs;
            },

            /**
             * Run event callbacks
             * @param {string} event_name The name of the events to run
             */
            'events': function () {
                if (arguments.length > 0) {
                    var event_name = arguments[0];
                    var info = this.getInformation();
                    var args = Array.prototype.slice.call(arguments, 1);

                    if (info && info.events[event_name]) {
                        $.each(info.events[event_name], function () {
                            this.apply(element, args);
                        });
                    }
                }
            },

            /**
             * Assign an event handler
             * @param  {string} event   the event type
             * @param  {function} handler the handler for the event
             */
            'on': function (event, handler) {
                var info = this.getInformation();

                if (!info.events[event]) info.events[event] = [];

                info.events[event].push(handler);

                this.saveInformation(info);
            },

            /**
             * Remove an event handler
             * @param  {string} event   the event type
             * @param  {function} handler the handler for the event
             */
            'off': function (event, handler) {
                var info = this.getInformation();
                if (info.events[event]) {
                    var index = info.events[event].indexOf(handler);

                    if (index > -1) {
                        info.events[event].splice(index, 1);
                    }
                }

                this.saveInformation(info);
            },

            /**
             * Grab the method runner
             * @return {object} Method runner.
             */
            'methodRunner': function() {
                return run;
            },

            /**
             * Completely reset this instance of the list plugin
             */
            'reset': function() {
                // Get our info and key, as we can't get it later
                var key = this.getKey();
                var info = this.getInformation();

                // Remove the entry in our registry
                delete global_information[key];

                // Remove all data first
                $.each(element.data(), function(name) {
                    element.removeData(name);
                });

                // Re-assign original data
                $.each(info.original.data, function(name, val) {
                    element.data(name, val);
                });

                // Remove all attributes first
                $.each(this.attrs(element[0]), function(name) {
                    if (name !== 'type') {
                        element.removeAttr(name);
                    }
                });

                // Re-assign original attributes
                $.each(info.original.attrs, function(name, val) {
                    if (name !== 'type') {
                        element.attr(name, val);
                    }
                });

                // Reset the html
                element.html(info.original.html);
            },

            /**
             * Get the template
             *
             * @param {bool} return_func Return the template function.
             * @return {string|function} template string or function, depending on return_func
             */
            'getTemplate': function(return_func) {
                var info = this.getInformation();

                return typeof return_func === 'undefined' ? info.template_html : info.template;
            },

            /**
             * Set a template, pass either an element or a string...
             *
             * @param {string|object} Template to set
             */
            'setTemplate': function(string) {
                if (typeof string !== 'string') {
                    string = string.html();
                }

                string = this.giveAttrs(string);

                var $copy = $('<div/>').html(string);
                var $elements;

                var handler = function($elements) {
                    $elements.each(function () {
                        var $this = $(this);
                        var name = $this.data('template');
                        var template = compiled_templates[name];

                        template($this);
                    });
                };

                while (($elements = $copy.find('[data-template]')).length > 0) {
                    handler($elements);
                }

                string = $copy.html();

                var info = this.getInformation();
                info.template_html = string;
                info.template = swig.compile(string);
                this.saveInformation(info);
            }
        };

        var run = function () {
            var name = Array.prototype.shift.call(arguments);

            return methods[name].apply(run, arguments);
        };

        $.each(methods, function (name) {

            run[name] = function () {

                Array.prototype.unshift.call(arguments, name);
                var args = arguments;

                return run.apply(run, args);
            };
        });

        return run;
    };

    /**
     * Get the method from the arguments variable
     *
     * Usage: getMethod.apply(this, arguments);
     */
    var getMethod = function() {
        if (arguments.length < 1) {
            Array.prototype.push.call(arguments, 'init');
        }

        var method = Array.prototype.splice.call(arguments, 0, 1)[0];

        return [method, arguments];
    };

    $.list = getMethods(false);

    $.fn.list = function () {

        var arr = getMethod.apply(this, arguments);
        var method = arr[0];
        var args = arr[1];

        if (!this.data('ran')) {

            var selector;

            if (method !== 'init') {
                Array.prototype.unshift.call(args, method);
                method = 'init';
            }

            if (args.length < 1) {
                selector = '[type="text/swig"]';
            } else {
                selector = args[0];
                Array.prototype.splice.call(args, 0, 1);
            }

            Array.prototype.unshift.call(args, selector);
        }

        var result = getMethods(this)[method].apply(this, args);

        if (result) {
            return result;
        } else {
            return this;
        }
    };

})(window, document, jQuery, swig);<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var fs = require('fs');
var jsdom = require('jsdom');

// from http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
var voidTags = 'area, base, br, col, embed, hr, img, input, keygen, link, menuitem, meta, param, source, track, wbr'.split(', ');

function cleanContentSubTree (tree) {
  var output = '';
  var childOutput = '';
  if (tree._nodeName.indexOf('app-') === 0) {
    for (var i = 0, l = tree._childNodes.length; i < l; ++i) {
      childOutput += cleanContentSubTree(tree._childNodes[i]);
    }
    output += '<' + tree._nodeName + createAttributeString(tree) + '>' + childOutput + '</' + tree._nodeName + '>';
  }
  else if (tree._nodeName === '#text') {
    output += tree._nodeValue.replace(/[^\s]/g, '');
  }
  return output;
}

function createAttributeString (tree) {
  var output = '';
  if (tree._attributes.length) {
    Object.keys(tree._attributes._nodes).forEach(function (name) {
      if (name !== 'style' && name.indexOf('on') !== 0 && name.indexOf('On') !== 0) {
        output += ' ' + name + '="' +  tree._attributes._nodes[name]._nodeValue + '"';
      }
    });
  }
  if (tree._style) {
    output += ' style="' + tree._style + '"';
  }
  return output;
}

function generateOpenTag (tree, childData) {
  var output = '';
  output += '<' + tree._nodeName;
  output += createAttributeString(tree);
  output += '>';
  return output;
}

function generateCloseTag (tree, childData) {
  var output = '';
  if (!!childData || voidTags.indexOf(tree._nodeName) === -1) {
    output += '</' + tree._nodeName + '>';
  }
  return output;
}

function checkTag (tree, tagName, attributes) {
  if (!tree || tree._nodeName !== tagName) {
    throw 'Invalid tag.';
  }
  Object.keys(attributes).forEach(function (attr) {
    if (attr === 'id') {
      if (attributes[attr] !== tree._attributes._nodes[attr]._nodeValue) {
        throw 'Attribute "' + attr + '" does not match: ' + attributes[attr];
      }
    }
    else {
      if (tree._attributes._nodes[attr]._nodeValue.indexOf(attributes[attr]) === -1) {
        throw 'Attribute "' + attr + '" does not match: ' + attributes[attr] + ', ' + tree._attributes._nodes[attr]._nodeValue;
      }
    }
  });
}

function siftThroughChildren (childNodes, childFunction) {
  var output = '';
  Array.prototype.forEach.call(childNodes, function (child) {
    if (child._nodeName === '#text') {
      output += child._nodeValue.replace(/[^\s]/g, '');
    }
    else {
      output += childFunction(child);
    }
  });
  return output;
}

function filterApp (tree) {
  checkTag(tree, 'div', { id: 'flathead-app' });
  var output = generateOpenTag(tree);
  var childOutput = '';

  if (tree._childNodes) {
    childOutput += siftThroughChildren(tree._childNodes, filterCard);
  }
  else {
    throw "No children!";
  }

  return output + childOutput + generateCloseTag(tree);
}

function filterCard (tree) {
  checkTag(tree, 'div', { class: 'ceci-card' });
  var output = generateOpenTag(tree);
  var childOutput = '';
  var childClasses = ['fixed-top', 'phone-canvas', 'fixed-bottom'];
  if (tree._childNodes) {
    childOutput += siftThroughChildren(tree._childNodes, function (child) {
      return filterSection(child, childClasses.shift());
    });
  }
  else {
    throw "No children!";
  }
  return output + childOutput + generateCloseTag(tree);
}

function filterSection (tree, name) {
  checkTag(tree, 'div', { class: name });
  var output = generateOpenTag(tree);
  var childOutput = '';
  if (tree._childNodes) {
    childOutput += siftThroughChildren(tree._childNodes, filterComponent);
  }
  return output + childOutput + generateCloseTag(tree);
}

function filterBroadcast (tree) {
  var output = '<' + tree._nodeName;
  output += siftThroughChildren(tree._childNodes, function (child) {
    if (child._attributes._nodes['color']) {
      return ' color="' + child._attributes._nodes['color']._nodeValue + '"';
    }
    return '';
  });
  return output + '></' + tree._nodeName + '>';
}

function filterListen (tree) {
  var output = '';
  var color = null;
  siftThroughChildren(tree._childNodes, function (child) {
    console.log(child._attributes._nodes['color']);
    if (child._attributes._nodes['color']) {
      color = child._attributes._nodes['color']._nodeValue
    }
  });
  if (color) {
    return '<' + tree._nodeName + '></' + tree._nodeName + '>';
  }
  else {
    return '';
  }
}

function filterComponent (tree) {
  var output = '';
  if (tree._nodeName.indexOf('app-') === 0) {
    output = generateOpenTag(tree);
    var childOutput = '';
    if (tree._childNodes) {
      childOutput += siftThroughChildren(tree._childNodes, filterComponent);
    }
    return output + childOutput + generateCloseTag(tree);
  }
  else if (tree._nodeName === 'broadcast') {
    output += filterBroadcast(tree);
  }
  else if (tree._nodeName === 'listen') {
    output += filterListen(tree);
  }
  return output;
}

module.exports = {
  filter: function (html, callback) {
    html = html.replace(/<script>[\s.]*<\/script>/g, '');

    jsdom.env(html, {
      done: function (errors, window) {
        var inputDocument = window.document;
        var inputNode = inputDocument.firstChild;
        var output = null;

        var appDiv = inputNode.firstChild.firstChild;

        try {
          output = filterApp(appDiv);
        }
        catch (e) {
          console.error(e);
          throw e;
        }

        callback(output);
      }
    });
  }
};<commit_msg>using DOM instead of string building<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var fs = require('fs');
var jsdom = require('jsdom');

function cleanTag (tree) {
  cleanAttributes(tree);
}

function cleanAttributes (tree, acceptableAttribtues) {
  var attributesToRemove = [];

  for (var i = 0, l = tree.attributes.length; i < l; ++i) {
    var name = tree.attributes[i].name;
    if ( (acceptableAttribtues && acceptableAttribtues.indexOf(name) === -1)
      || ( name !== 'on' &&
         ( name.indexOf('on') === 0 || name.indexOf('On') === 0 || name.indexOf('oN') === 0
        || name.indexOf('ON') === 0) ) ) {
      attributesToRemove.push(name);
    }
  }
  while (attributesToRemove.length > 0) {
    tree.removeAttribute(attributesToRemove.shift());
  }
}

function cleanContentSubTree (tree) {
  var output = '';
  var childOutput = '';
  if (tree._nodeName.indexOf('app-') === 0) {
    for (var i = 0, l = tree._childNodes.length; i < l; ++i) {
      childOutput += cleanContentSubTree(tree._childNodes[i]);
    }
    output += '<' + tree._nodeName + createAttributeString(tree) + '>' + childOutput + '</' + tree._nodeName + '>';
  }
  else if (tree._nodeName === '#text') {
    output += tree._nodeValue.replace(/[^\s]/g, '');
  }
  return output;
}

function checkTag (tree, tagName, attributes) {
  if (!tree || tree._nodeName !== tagName) {
    throw 'Invalid tag.';
  }

  var attr;
  attributes = attributes || [];
  Object.keys(attributes).forEach(function (name) {
    var expectedValue = attributes[name];
    var inputAttribute = tree.attributes[name];

    if (name === 'id') {
      if (expectedValue !== inputAttribute.value) {
        throw 'Attribute "' + 'id' + '" does not match: ' + attributes[name];
      }
    }
    else {
      if (inputAttribute.value.indexOf(expectedValue) === -1) {
        throw 'Attribute "' + name + '" does not match: ' + expectedValue + ', ' + inputAttribute.value;
      }
    }
  });
}

function siftThroughChildren (childNodes, childFunction) {
  var childrenToRemove = [];
  Array.prototype.forEach.call(childNodes, function (child) {
    if (child._nodeName === '#text') {
      child._nodeValue.replace(/[^\s]/g, '');
    }
    else {
      if (!childFunction(child)) {
        childrenToRemove.push(child);
      }
    }
  });
  while (childrenToRemove.length > 0) {
    var child = childrenToRemove.shift();
    child.parentNode.removeChild(child);
  }
}

function filterApp (tree) {
  checkTag(tree, 'div', { id: 'flathead-app' });
  cleanTag(tree);

  if (tree._childNodes) {
    siftThroughChildren(tree._childNodes, filterCard);
  }
  else {
    throw "No children!";
  }

  return tree;
}

function filterCard (tree) {
  checkTag(tree, 'div', { class: 'ceci-card' });
  cleanTag(tree);

  var childClasses = ['fixed-top', 'phone-canvas', 'fixed-bottom'];
  if (tree._childNodes) {
    siftThroughChildren(tree._childNodes, function (child) {
      return filterSection(child, childClasses.shift());
    });
    return true;
  }
  else {
    throw "No children!";
  }
}

function filterSection (tree, name) {
  checkTag(tree, 'div', { class: name });
  cleanTag(tree);

  if (tree._childNodes) {
    siftThroughChildren(tree._childNodes, filterComponent);
  }
  return true;
}

function filterSubscription (tree) {
  cleanAttributes(tree, ['on', 'for']);
  siftThroughChildren(tree._childNodes, function (child) {
    cleanAttributes(child, ['color']);
  });
  return true;
}

function filterComponent (tree) {
  if (tree._nodeName.indexOf('app-') === 0) {
    if (tree._childNodes) {
      siftThroughChildren(tree._childNodes, filterComponent);
      return true;
    }
  }
  else if (['broadcast', 'listen'].indexOf(tree._nodeName) > -1) {
    return filterSubscription(tree);
  }

  return false;
}

module.exports = {
  filter: function (html, callback) {
    html = html.replace(/<script>[\s.]*<\/script>/g, '');

    jsdom.env(html, {
      done: function (errors, window) {
        var inputDocument = window.document;
        var inputNode = inputDocument.firstChild;
        var output = null;

        var appDiv = inputNode.firstChild.firstChild;

        try {
          output = filterApp(appDiv).outerHTML;
        }
        catch (e) {
          console.error(e);
          throw e;
        }

        callback(output);
      }
    });
  }
};<|endoftext|>


<commit_before>(function rocketbeltProgressIndicators(rb, document) {
  function onClassMutation(mutations) {
    var mutationsLen = mutations && mutations.length ? mutations.length : 0;

    for (var k = 0; k < mutationsLen; k++) {
      var mutation = mutations[k];
      var el = mutation.target;

      if (mutation.target.classList.contains('is-busy')) {
        // If "is-busy" was added, do the decoratin'
		if (mutation.target.getElementsByClassName('is-busy_overlay').length === 0) {
		// Only add overlay if one doesn't already exist
			var dot = '

';
			var a11yAttrs = 'aria-label="Loading." role="alert" aria-live="polite" aria-busy="true"';
			var elType = el.nodeName === 'UL' || el.nodeName === 'OL' ? 'li' : 'div';
			var fragment = '<' + elType + ' class="is-busy_overlay is-busy_overlay-opaque" ' + a11yAttrs + '>' + dot + dot + dot + '</' + elType + '>';

			el.innerHTML = el.innerHTML + fragment;
		}
      } else {
        // If "is-busy" was removed
        var overlay = el.querySelectorAll('.is-busy_overlay')[0];

        if (overlay && !overlay.parentNode.classList.contains('is-busy')) {
		overlay.parentNode.removeChild(overlay);
	}
      }
    }
  }

  function observeComponents() {
    var busyable = document.querySelectorAll('.is-busyable');

    if (busyable.length === 0) {
      busyable = document.querySelectorAll('body');
    }

    var busyableLen = busyable.length;

    for (var i = 0; i < busyableLen; i++) {
      var busyableEl = busyable[i];

      // Set an observer to listen for .invalid.
      var observer = new MutationObserver(function (mutations) { onClassMutation(mutations); });
      observer.observe(busyableEl, { subtree: true, attributes: true, attributeOldValue: true, attributeFilter: ['class'] });
    }
  }

  rb.onDocumentReady(observeComponents);
  rb.progressIndicators = rb.progressIndicators || {};
  rb.progressIndicators.observeComponents = observeComponents;
})(window.rb, document);
<commit_msg>verifies classList exists<commit_after>(function rocketbeltProgressIndicators(rb, document) {
  function onClassMutation(mutations) {
    var mutationsLen = mutations && mutations.length ? mutations.length : 0;

    for (var k = 0; k < mutationsLen; k++) {
      var mutation = mutations[k];
      var el = mutation.target;

      if (mutation.target.classList && mutation.target.classList.contains('is-busy')) {
        // If "is-busy" was added, do the decoratin'
		if (mutation.target.getElementsByClassName('is-busy_overlay').length === 0) {
		// Only add overlay if one doesn't already exist
			var dot = '

';
			var a11yAttrs = 'aria-label="Loading." role="alert" aria-live="polite" aria-busy="true"';
			var elType = el.nodeName === 'UL' || el.nodeName === 'OL' ? 'li' : 'div';
			var fragment = '<' + elType + ' class="is-busy_overlay is-busy_overlay-opaque" ' + a11yAttrs + '>' + dot + dot + dot + '</' + elType + '>';

			el.innerHTML = el.innerHTML + fragment;
		}
      } else {
        // If "is-busy" was removed
        var overlay = el.querySelectorAll('.is-busy_overlay')[0];

        if (overlay && !overlay.parentNode.classList.contains('is-busy')) {
		overlay.parentNode.removeChild(overlay);
	}
      }
    }
  }

  function observeComponents() {
    var busyable = document.querySelectorAll('.is-busyable');

    if (busyable.length === 0) {
      busyable = document.querySelectorAll('body');
    }

    var busyableLen = busyable.length;

    for (var i = 0; i < busyableLen; i++) {
      var busyableEl = busyable[i];

      // Set an observer to listen for .invalid.
      var observer = new MutationObserver(function (mutations) { onClassMutation(mutations); });
      observer.observe(busyableEl, { subtree: true, attributes: true, attributeOldValue: true, attributeFilter: ['class'] });
    }
  }

  rb.onDocumentReady(observeComponents);
  rb.progressIndicators = rb.progressIndicators || {};
  rb.progressIndicators.observeComponents = observeComponents;
})(window.rb, document);
<|endoftext|>


<commit_before>(function(a){var b=Array.isArray||function(a){return Object.prototype.toString.call(a)==="[object Array]"},c=function(a){return Object.prototype.toString.call(a)==="[object Object]"},d=Object.keys||function(a){var b=[],c="";for(c in a){if(a.hasOwnProperty(c))b.push(c)}return b},e=function(b,c){return b==a?c:b};_cookie={set:function(a,b,d){if(c(a)){for(var e in a){this.set(e,a[e])}}else{d=d||{};var f=d.expires||"",g=typeof f,h=d.path?";path="+d.path:"",i=d.domain?";domain="+d.domain:"",j=d.secure?";secure":"";if(f!==""&&g=="string")f=";expires="+f;else if(g=="number"){var k=new Date;k.setTime(k.getTime()+f);f=";expires="+k.toGMTString()}else if(f.hasOwnProperty("toGMTString"))f=";expires="+f.toGMTString();document.cookie=a+"="+escape(b)+f+h+i+j}return this},remove:function(a){var c=b(a)?a:arguments;for(var d=0,e=c.length;d<e;d++){this.set(c[d],"",{expires:-60*60*24})}return this},empty:function(){return this.remove(d(this.all()))},get:function(c,d){d=d||a;if(b(c)){var f={},g=this.all();for(var h=0,i=c.length;h<i;h++){var j=c[h];f[j]=e(g[j],d)}return f}else{var g=this.all();return e(g[c],d)}},all:function(){if(document.cookie=="")return{};var a=document.cookie.split("; "),b={};for(var c=0,d=a.length;c<d;c++){var e=a[c].split("=");b[e[0]]=unescape(e[1])}return b}},cookie=function(a,b){return _cookie.get(a,b)},methods=["set","remove","empty","get","all"];for(var f=0,g=methods.length;f<g;f++){var h=methods[f];cookie[h]=_cookie[h]}window.cookie=cookie})()<commit_msg>google closure compressor is now being used to minify the code (this made the code a little bit smaller)  and I added a new method cookie.enabled() that checks if cookies are enabled<commit_after>(function(h){function f(a,d){return j.get(a,d)}for(var k=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)},n=Object.keys||function(a){var d=[],b="";for(b in a)a.hasOwnProperty(b)&&d.push(b);return d},j={set:function(a,d,b){if("[object Object]"===Object.prototype.toString.call(a))for(var c in a)a.hasOwnProperty(c)&&this.set(c,a[c]);else{b=b||{};c=b.a||"";var e=typeof c,f=b.path?";path="+b.path:"",g=b.domain?";domain="+b.domain:"",b=b.b?";secure":"";"string"===e&&""!==c?c=";expires="+c:"number"==e?(e=new Date,e.setTime(e.getTime()+c),c=";expires="+e.toGMTString()):c.hasOwnProperty("toGMTString")&&(c=";expires="+c.toGMTString());document.cookie=a+"="+escape(d)+c+f+g+b}return this},remove:function(a){for(var a=k(a)?a:arguments,d=0,b=a.length;d<b;d++)this.set(a[d],"",{a:-86400});return this},empty:function(){return this.remove(n(this.all()))},get:function(a,d){d=d||h;if(k(a)){for(var b={},c=this.all(),e=0,f=a.length;e<f;e++){var g=a[e];b[g]=c[g]===h?d:c[g]}return b}c=this.all();return c[a]===h?d:c[a]},all:function(){if(""==document.cookie)return{};for(var a=document.cookie.split("; "),d={},b=0,c=a.length;b<c;b++){var e=a[b].split("=");d[e[0]]=unescape(e[1])}return d},enabled:function(){var a="b"===f.set("a","b").get("a");f.remove("a");return a}},l="set,remove,empty,get,all,enabled".split(","),i=0,o=l.length;i<o;i++){var m=l[i];f[m]=j[m]}window.cookie=f})();<|endoftext|>


<commit_before>var THREE = require('three');

/**
 * Zinc representation of glyph graphic, it contains the colours,
 * geometry and transformation of the glyph.
 *
 * @param {THREE.Geometry} geometry - Geometry of the glyph.
 * @param {THREE.material} materialIn - Material of the glyph.
 * @param {Number} idIn - Ud of the glyph.
 *
 * @class
 * @author Alan Wu
 * @return {Zinc.Glyph}
 */
exports.Glyph = function(geometry, materialIn, idIn)  {
	var material = materialIn.clone();
	material.vertexColors = THREE.FaceColors;
	var mesh = new THREE.Mesh( geometry, material );

	this.id = idIn;
	var _this = this;

	/**
	 * Get the mesh of this glyph.
	 * @return {THREE.Mesh}
	 */
	this.getMesh = function () {
		return mesh;
	}

	/**
	 * Get the bounding box of this glyph.
	 * @return {THREE.Box3}
	 */
	this.getBoundingBox = function() {
		if (mesh)
			return new THREE.Box3().setFromObject(mesh);
		return undefined;
	}

	/**
	 * Set the Colour of this glyph.
	 * @param {THREE.Color} colorIn - Colour to be set of this mesh.
	 */
	this.setColor = function (colorIn) {
		mesh.material.color = colorIn;
		mesh.geometry.colorsNeedUpdate = true;
	}

	/**
	 * Set the transformation of this glyph.
	 * @param {Array} position - Three components vectors containing position of the
	 * transformation.
	 * @param {Array} axis1 - Three components vectors containing axis1 rotation of the
	 * transformation.
	 * @param {Array} axis2 - Three components vectors containing axis2 rotation of the
	 * transformation.
	 * @param {Array} position - Three components vectors containing axis3 rotation of the
	 * transformation.
	 */
	this.setTransformation = function(position, axis1, axis2, axis3) {
		mesh.matrix.elements[0] = axis1[0];
		mesh.matrix.elements[1] = axis1[1];
		mesh.matrix.elements[2] = axis1[2];
		mesh.matrix.elements[3] = 0.0;
		mesh.matrix.elements[4] = axis2[0];
		mesh.matrix.elements[5] = axis2[1];
		mesh.matrix.elements[6] = axis2[2];
		mesh.matrix.elements[7] = 0.0;
		mesh.matrix.elements[8] = axis3[0];
		mesh.matrix.elements[9] = axis3[1];
		mesh.matrix.elements[10] = axis3[2];
		mesh.matrix.elements[11] = 0.0;
		mesh.matrix.elements[12] = position[0];
		mesh.matrix.elements[13] = position[1];
		mesh.matrix.elements[14] = position[2];
		mesh.matrix.elements[15] = 1.0;
		mesh.matrixAutoUpdate = false;
	}

	/**
	 * Clear and free its memory.
	 */
	this.dispose = function() {
		_this.material.dispose();
		_this.mesh = undefined;
	}
}
<commit_msg>Fix a bug causing glyph to fail at dispose function call.<commit_after>var THREE = require('three');

/**
 * Zinc representation of glyph graphic, it contains the colours,
 * geometry and transformation of the glyph.
 *
 * @param {THREE.Geometry} geometry - Geometry of the glyph.
 * @param {THREE.material} materialIn - Material of the glyph.
 * @param {Number} idIn - Ud of the glyph.
 *
 * @class
 * @author Alan Wu
 * @return {Zinc.Glyph}
 */
exports.Glyph = function(geometry, materialIn, idIn)  {
	var material = materialIn.clone();
	material.vertexColors = THREE.FaceColors;
	var mesh = new THREE.Mesh( geometry, material );

	this.id = idIn;
	var _this = this;

	/**
	 * Get the mesh of this glyph.
	 * @return {THREE.Mesh}
	 */
	this.getMesh = function () {
		return mesh;
	}

	/**
	 * Get the bounding box of this glyph.
	 * @return {THREE.Box3}
	 */
	this.getBoundingBox = function() {
		if (mesh)
			return new THREE.Box3().setFromObject(mesh);
		return undefined;
	}

	/**
	 * Set the Colour of this glyph.
	 * @param {THREE.Color} colorIn - Colour to be set of this mesh.
	 */
	this.setColor = function (colorIn) {
		mesh.material.color = colorIn;
		mesh.geometry.colorsNeedUpdate = true;
	}

	/**
	 * Set the transformation of this glyph.
	 * @param {Array} position - Three components vectors containing position of the
	 * transformation.
	 * @param {Array} axis1 - Three components vectors containing axis1 rotation of the
	 * transformation.
	 * @param {Array} axis2 - Three components vectors containing axis2 rotation of the
	 * transformation.
	 * @param {Array} position - Three components vectors containing axis3 rotation of the
	 * transformation.
	 */
	this.setTransformation = function(position, axis1, axis2, axis3) {
		mesh.matrix.elements[0] = axis1[0];
		mesh.matrix.elements[1] = axis1[1];
		mesh.matrix.elements[2] = axis1[2];
		mesh.matrix.elements[3] = 0.0;
		mesh.matrix.elements[4] = axis2[0];
		mesh.matrix.elements[5] = axis2[1];
		mesh.matrix.elements[6] = axis2[2];
		mesh.matrix.elements[7] = 0.0;
		mesh.matrix.elements[8] = axis3[0];
		mesh.matrix.elements[9] = axis3[1];
		mesh.matrix.elements[10] = axis3[2];
		mesh.matrix.elements[11] = 0.0;
		mesh.matrix.elements[12] = position[0];
		mesh.matrix.elements[13] = position[1];
		mesh.matrix.elements[14] = position[2];
		mesh.matrix.elements[15] = 1.0;
		mesh.matrixAutoUpdate = false;
	}

	/**
	 * Clear and free its memory.
	 */
	this.dispose = function() {
	  if (_this.material)
	    _this.material.dispose();
		_this.mesh = undefined;
	}
}
<|endoftext|>


<commit_before>var keystone = require('keystone'),
    Types = keystone.Field.Types;

// Create model. Additional options allow menu name to be used what auto-generating URLs
var InternalNotes = new keystone.List('Internal Note', {
    track: true,
    autokey: { path: 'key', from: 'slug', unique: true },
    defaultSort: 'date'
});

// Create fields
InternalNotes.add({

    child: { type: Types.Relationship, label: 'child', ref: 'Child', initial: true, },
    family: { type: Types.Relationship, label: 'family', ref: 'Prospective Parent or Family', initial: true },
    date: { type: Types.Text, label: 'note date', note: 'mm/dd/yyyy', required: true, noedit: true, initial: true, },
    employee: { type: Types.Relationship, label: 'note creator', ref: 'User', required: true, noedit: true, initial: true, },
    note: { type: Types.Textarea, label: 'note', required: true, initial: true, }

});

// Pre Save
InternalNotes.schema.pre('save', function(next) {
    'use strict';

    // generate an internal ID based on the current highest internal ID
    // get the employee who is currently logged in and save ID to employee

    // TODO: Assign a registration number if one isn't assigned
    next();
});

// Define default columns in the admin interface and register the model
InternalNotes.defaultColumns = 'date, child, family, note';
InternalNotes.register();<commit_msg>Fix a bad reference value in the Internal Note model.<commit_after>var keystone = require('keystone'),
    Types = keystone.Field.Types;

// Create model. Additional options allow menu name to be used what auto-generating URLs
var InternalNotes = new keystone.List('Internal Note', {
    track: true,
    autokey: { path: 'key', from: 'slug', unique: true },
    defaultSort: 'date'
});

// Create fields
InternalNotes.add({

    child: { type: Types.Relationship, label: 'child', ref: 'Child', initial: true, },
    prospectiveParentOrFamily: { type: Types.Relationship, label: 'family', ref: 'Prospective Parent or Family', initial: true },
    date: { type: Types.Text, label: 'note date', note: 'mm/dd/yyyy', required: true, noedit: true, initial: true, },
    employee: { type: Types.Relationship, label: 'note creator', ref: 'User', required: true, noedit: true, initial: true, },
    note: { type: Types.Textarea, label: 'note', required: true, initial: true, }

});

// Pre Save
InternalNotes.schema.pre('save', function(next) {
    'use strict';

    // generate an internal ID based on the current highest internal ID
    // get the employee who is currently logged in and save ID to employee

    // TODO: Assign a registration number if one isn't assigned
    next();
});

// Define default columns in the admin interface and register the model
InternalNotes.defaultColumns = 'date, child, family, note';
InternalNotes.register();<|endoftext|>


<commit_before>/* @flow */

import path from 'path';
import morgan from 'morgan';
import express from 'express';
import compression from 'compression';
import helmet from 'helmet';
import hpp from 'hpp';
import favicon from 'serve-favicon';
import React from 'react';
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
import { StaticRouter, matchPath } from 'react-router-dom';
import { Provider } from 'react-redux';
import chalk from 'chalk';

import createHistory from 'history/createMemoryHistory';
import configureStore from './redux/store';
import Html from './utils/Html';
import App from './containers/App';
import routes from './routes';
import { port, host } from './config';

const app = express();

// Using helmet to secure Express with various HTTP headers
app.use(helmet());
// Prevent HTTP parameter pollution.
app.use(hpp());
// Compress all requests
app.use(compression());

// Use morgan for http request debug (only show error)
app.use(morgan('dev', { skip: (req, res) => res.statusCode < 400 }));
app.use(favicon(path.join(process.cwd(), './public/favicon.ico')));
app.use(express.static(path.join(process.cwd(), './public')));

// Run express as webpack dev server
if (__DEV__) {
  const webpack = require('webpack');
  const webpackConfig = require('../tools/webpack/config.babel');

  const compiler = webpack(webpackConfig);

  app.use(
    require('webpack-dev-middleware')(compiler, {
      publicPath: webpackConfig.output.publicPath,
      hot: true,
      noInfo: true,
      stats: { colors: true },
      serverSideRender: true
    })
  );

  app.use(require('webpack-hot-middleware')(compiler));
}

// Register server-side rendering middleware
app.get('*', (req, res) => {
  if (__DEV__) webpackIsomorphicTools.refresh();

  const history = createHistory();
  const store = configureStore(history);
  // eslint-disable-next-line no-shadow
  const renderHtml = (store, htmlContent) => {
    const html = renderToStaticMarkup(
      <Html store={store} htmlContent={htmlContent} />
    );

    return `<!doctype html>${html}`;
  };

  // If __DISABLE_SSR__ = true, disable server side rendering
  if (__DISABLE_SSR__) {
    res.send(renderHtml(store));
    return;
  }

  // Here's the method for loading data from server-side
  const loadBranchData = (): Promise<*> | Object => {
    const promises = [];

    routes.some(route => {
      const match = matchPath(req.path, route);

      if (match && route.loadData)
        // $FlowFixMe: the params of pre-load actions are dynamic
        promises.push(route.loadData(store.dispatch, match.params));

      return match;
    });

    return Promise.all(promises);
  };

  (async () => {
    try {
      // Load data from server-side first
      await loadBranchData();

      // Setup React-Router server-side rendering
      const routerContext = {};
      const htmlContent = renderToString(
        <Provider store={store}>
          <StaticRouter location={req.url} context={routerContext}>
            <App />
          </StaticRouter>
        </Provider>
      );

      // Check if the render result contains a redirect, if so we need to set
      // the specific status and redirect header and end the response
      if (routerContext.url) {
        res.status(301).setHeader('Location', routerContext.url);
        res.end();

        return;
      }

      // Checking is page is 404
      const status = routerContext.status === '404' ? 404 : 200;

      // Pass the route and initial state into html template
      res.status(status).send(renderHtml(store, htmlContent));
    } catch (err) {
      res.status(404).send('Not Found :(');

      console.error(`==> 😭  Rendering routes error: ${err}`);
    }
  })();
});

if (port) {
  app.listen(port, host, err => {
    const url = `http://${host}:${port}`;

    if (err) console.error(`==> 😭  OMG!!! ${err}`);

    console.info(chalk.green(`==> 🌎  Listening at ${url}`));

    // Open Chrome
    require('../tools/openBrowser')(url);
  });
} else {
  console.error(
    chalk.red('==> 😭  OMG!!! No PORT environment variable has been specified')
  );
}
<commit_msg>Refine webpack-dev-middleware config<commit_after>/* @flow */

import path from 'path';
import morgan from 'morgan';
import express from 'express';
import compression from 'compression';
import helmet from 'helmet';
import hpp from 'hpp';
import favicon from 'serve-favicon';
import React from 'react';
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
import { StaticRouter, matchPath } from 'react-router-dom';
import { Provider } from 'react-redux';
import chalk from 'chalk';

import createHistory from 'history/createMemoryHistory';
import configureStore from './redux/store';
import Html from './utils/Html';
import App from './containers/App';
import routes from './routes';
import { port, host } from './config';

const app = express();

// Using helmet to secure Express with various HTTP headers
app.use(helmet());
// Prevent HTTP parameter pollution.
app.use(hpp());
// Compress all requests
app.use(compression());

// Use morgan for http request debug (only show error)
app.use(morgan('dev', { skip: (req, res) => res.statusCode < 400 }));
app.use(favicon(path.join(process.cwd(), './public/favicon.ico')));
app.use(express.static(path.join(process.cwd(), './public')));

// Run express as webpack dev server
if (__DEV__) {
  const webpack = require('webpack');
  const webpackConfig = require('../tools/webpack/config.babel');

  const compiler = webpack(webpackConfig);

  app.use(
    require('webpack-dev-middleware')(compiler, {
      publicPath: webpackConfig.output.publicPath,
      hot: true,
      noInfo: true,
      stats: 'minimal',
      serverSideRender: true
    })
  );

  app.use(require('webpack-hot-middleware')(compiler));
}

// Register server-side rendering middleware
app.get('*', (req, res) => {
  if (__DEV__) webpackIsomorphicTools.refresh();

  const history = createHistory();
  const store = configureStore(history);
  // eslint-disable-next-line no-shadow
  const renderHtml = (store, htmlContent) => {
    const html = renderToStaticMarkup(
      <Html store={store} htmlContent={htmlContent} />
    );

    return `<!doctype html>${html}`;
  };

  // If __DISABLE_SSR__ = true, disable server side rendering
  if (__DISABLE_SSR__) {
    res.send(renderHtml(store));
    return;
  }

  // Here's the method for loading data from server-side
  const loadBranchData = (): Promise<*> | Object => {
    const promises = [];

    routes.some(route => {
      const match = matchPath(req.path, route);

      if (match && route.loadData)
        // $FlowFixMe: the params of pre-load actions are dynamic
        promises.push(route.loadData(store.dispatch, match.params));

      return match;
    });

    return Promise.all(promises);
  };

  (async () => {
    try {
      // Load data from server-side first
      await loadBranchData();

      // Setup React-Router server-side rendering
      const routerContext = {};
      const htmlContent = renderToString(
        <Provider store={store}>
          <StaticRouter location={req.url} context={routerContext}>
            <App />
          </StaticRouter>
        </Provider>
      );

      // Check if the render result contains a redirect, if so we need to set
      // the specific status and redirect header and end the response
      if (routerContext.url) {
        res.status(301).setHeader('Location', routerContext.url);
        res.end();

        return;
      }

      // Checking is page is 404
      const status = routerContext.status === '404' ? 404 : 200;

      // Pass the route and initial state into html template
      res.status(status).send(renderHtml(store, htmlContent));
    } catch (err) {
      res.status(404).send('Not Found :(');

      console.error(`==> 😭  Rendering routes error: ${err}`);
    }
  })();
});

if (port) {
  app.listen(port, host, err => {
    const url = `http://${host}:${port}`;

    if (err) console.error(`==> 😭  OMG!!! ${err}`);

    console.info(chalk.green(`==> 🌎  Listening at ${url}`));

    // Open Chrome
    require('../tools/openBrowser')(url);
  });
} else {
  console.error(
    chalk.red('==> 😭  OMG!!! No PORT environment variable has been specified')
  );
}
<|endoftext|>


<commit_before>// Controller
// ==========
// Base controller class. Extend with an initialize method
// and actions (methods) to handle requests.

module.exports = Controller

var EventEmitter = require('events').EventEmitter
var _ = require('underscore')
var async = require('async')
var extendable = require('extendable')
var lifecycle = require('./lifecycle')
var Plugin = require('../plugin')

function Controller(options) {
  this.res = options.res
  this.req = options.req
  this.config = options.config
  this.route = options.route
  this.log = this.req.log

  this.templateOptions = _.extend({}, this.templateOptions)

  var args = Array.prototype.slice.call(arguments)

  // setup as an event emitter
  EventEmitter.call(this)

  if (this.runPlugins) {
    // run plugin setup for this controller instance
    this.log.info('Running plugins')
    this.runPlugins(afterPlugins.bind(this))
  }
  else {
    afterplugins.bind(this)()
  }

  function afterPlugins(err) {
    // if the response has already finished, abandon the rest
    if (this.res.finished) {
      this.log.info('A plugin terminated the response.')
      return
    }

    // setup request timeout handler
    this.req.on('timeout', this.timeout.bind(this))

    this.model = {}

    this._done = this._done.bind(this)
    options.done = this._done

    this._defaultMediaType = this.config && this.config.defaultMediaType || 'text/html'

    this.log.info('Running controller\'s initialize function')
    this.initialize.apply(this, args)

    this.runBefore(function(err) {
      if (err) return this.error(err)
      var parallelFuncs = [this.runDuring.bind(this)]
      // Call the `action` if one was matched in the route
      if (options.route.action) {
        this.log.info('Calling the controller\'s ' + options.route.action + ' action')
        var action = this[options.route.action]
        if (action) parallelFuncs.push(action.bind(this))
      }
      // run 'during' methods and the action if there is one
      async.parallel(parallelFuncs, this._done)
    }.bind(this))
  }
}

_.extend(Controller.prototype, EventEmitter.prototype, {

  // Called when the action method is done populating the
  // this.model object with data.
  _done: function() {
    if (this.res.finished) return
    this.runAfter(function(err) {
      if (err) return this.error(err)
      this._render()
    }.bind(this))
  },

  // Determines the best response type based on what the
  // client can accept and what the controller can output.
  _render: function() {
    var self = this

    var preferredType = this.mediaType()

    if (/html$/.test(preferredType)) {
      this.log.info('Rendering HTML')
      return this.render(function(err, html){
        if (err) self.error(err)
        if (self.html) return self.html(html)
        throw new Error('No `html` method implemented')
      })
    }

    if (/json$/.test(preferredType)) {
      this.log.info('Rendering JSON')
      if (this.json) return this.json(this.model)
      throw new Error('No `json` method implemented')
    }

    if (/xml$/.test(preferredType)) {
      this.log.info('Rendering XML')
      if (this.xml) return this.xml(this.model)
      throw new Error('No `xml` method implemented')
    }

    this.error(415, 'Media type not supported')
  },

  before: lifecycle.createRegFunction('before'),
  runBefore: lifecycle.createExecFunction('before'),
  during: lifecycle.createRegFunction('during'),
  runDuring: lifecycle.createExecFunction('during'),
  after: lifecycle.createRegFunction('after'),
  runAfter: lifecycle.createExecFunction('after'),

  // Determines the media type to respond with. Override
  // with your own content negotiation code.
  mediaType: function() {
    return this._defaultMediaType
  },

  // Initialize is an empty method by default. Override
  // it with your own initialization logic.
  initialize: function(){},

  // Handles a timed out request. Override with your
  // own logic and error handling.
  timeout: function() {
    this.error(504, 'Server timeout')
  },

  // content types that can be output from this controller
  availableMediaTypes: [
    'text/html',
    'application/json'
  ],

  // template settings
  template: null,
  templateOptions: {
    layout: 'layout'
  },

  // A stub for rendering content from a template. Override
  // with your own rendering code.
  render: function(callback) {
    return callback(null, '')
  },

  error: function() {
    var args = Array.prototype.slice.call(arguments)
    var status = 500
    var message = 'An error occured'
    args.forEach(function(v) {
      if (typeof v === 'string')
        message = v
      else if (typeof v === 'number')
        status = v
    })
    this.res.setHeader('content-type', 'text/plain')
    this.res.statusCode = status
    this.res.end(message)
  },

  // Call this in your initialize function to enforce
  // a maximum request size. Useful for file uploads.
  enforceMaxLength: function(maxLen) {
    // respond with errors if max length is exceeded
    if (typeof maxLen === 'number') {
      var cl = req.headers['content-length']
      res.setHeader('max-length', ''+maxLen)
      if (!cl) {
        res.error(411)  // length required
        return false
      }
      if (cl > req.maxLen) {
        res.error(413) // too long
        return false
      }
    }
    return true
  },

  // Transfers this request over to another controller
  transfer: function(controller, action, data) {
    // make sure the page can no longer render from this controller
    this._render = function(){}

    var self = this
    // use nextTick to let the stack clear before firing the event
    // since it's possible the handler won't be registered yet.
    process.nextTick(function(){
      // the app will be listening for a transfer event and
      // will create a new controller based on the current one
      self.emit('transfer', self, controller, action, data)
    })
  }

})

Controller.extend = extendable
Plugin.makePluggable(Controller)
<commit_msg>added beforePlugins method<commit_after>// Controller
// ==========
// Base controller class. Extend with an initialize method
// and actions (methods) to handle requests.

module.exports = Controller

var EventEmitter = require('events').EventEmitter
var _ = require('underscore')
var async = require('async')
var extendable = require('extendable')
var lifecycle = require('./lifecycle')
var Plugin = require('../plugin')

function Controller(options) {
  this.res = options.res
  this.req = options.req
  this.config = options.config
  this.route = options.route
  this.log = this.req.log

  this.templateOptions = _.extend({}, this.templateOptions)

  var args = Array.prototype.slice.call(arguments)

  // setup as an event emitter
  EventEmitter.call(this)

  this.log.info('Before plugins')
  this.beforePlugins.apply(this, args)

  if (this.runPlugins) {
    // run plugin setup for this controller instance
    this.log.info('Running plugins')
    this.runPlugins(afterPlugins.bind(this))
  }
  else {
    afterplugins.call(this)
  }

  function afterPlugins(err) {
    // if the response has already finished, abandon the rest
    if (this.res.finished) {
      this.log.info('A plugin terminated the response.')
      return
    }

    // setup request timeout handler
    this.req.on('timeout', this.timeout.bind(this))

    this.model = {}

    this._done = this._done.bind(this)
    options.done = this._done

    this._defaultMediaType = this.config && this.config.defaultMediaType || 'text/html'

    this.log.info('Running controller\'s initialize function')
    this.initialize.apply(this, args)

    this.runBefore(function(err) {
      if (err) return this.error(err)
      var parallelFuncs = [this.runDuring.bind(this)]
      // Call the `action` if one was matched in the route
      if (options.route.action) {
        this.log.info('Calling the controller\'s ' + options.route.action + ' action')
        var action = this[options.route.action]
        if (action) parallelFuncs.push(action.bind(this))
      }
      // run 'during' methods and the action if there is one
      async.parallel(parallelFuncs, this._done)
    }.bind(this))
  }
}

_.extend(Controller.prototype, EventEmitter.prototype, {

  // Called when the action method is done populating the
  // this.model object with data.
  _done: function() {
    if (this.res.finished) return
    this.runAfter(function(err) {
      if (err) return this.error(err)
      this._render()
    }.bind(this))
  },

  // Determines the best response type based on what the
  // client can accept and what the controller can output.
  _render: function() {
    var self = this

    var preferredType = this.mediaType()

    if (/html$/.test(preferredType)) {
      this.log.info('Rendering HTML')
      return this.render(function(err, html){
        if (err) self.error(err)
        if (self.html) return self.html(html)
        throw new Error('No `html` method implemented')
      })
    }

    if (/json$/.test(preferredType)) {
      this.log.info('Rendering JSON')
      if (this.json) return this.json(this.model)
      throw new Error('No `json` method implemented')
    }

    if (/xml$/.test(preferredType)) {
      this.log.info('Rendering XML')
      if (this.xml) return this.xml(this.model)
      throw new Error('No `xml` method implemented')
    }

    this.error(415, 'Media type not supported')
  },

  before: lifecycle.createRegFunction('before'),
  runBefore: lifecycle.createExecFunction('before'),
  during: lifecycle.createRegFunction('during'),
  runDuring: lifecycle.createExecFunction('during'),
  after: lifecycle.createRegFunction('after'),
  runAfter: lifecycle.createExecFunction('after'),

  // Determines the media type to respond with. Override
  // with your own content negotiation code.
  mediaType: function() {
    return this._defaultMediaType
  },

  // beforePlugins is an empty method by default and runs before
  // plugins are run. Override it with your own logic.
  beforePlugins: function() {},

  // Initialize is an empty method by default. Override
  // it with your own initialization logic.
  initialize: function(){},

  // Handles a timed out request. Override with your
  // own logic and error handling.
  timeout: function() {
    this.error(504, 'Server timeout')
  },

  // content types that can be output from this controller
  availableMediaTypes: [
    'text/html',
    'application/json'
  ],

  // template settings
  template: null,
  templateOptions: {
    layout: 'layout'
  },

  // A stub for rendering content from a template. Override
  // with your own rendering code.
  render: function(callback) {
    return callback(null, '')
  },

  error: function() {
    var args = Array.prototype.slice.call(arguments)
    var status = 500
    var message = 'An error occured'
    args.forEach(function(v) {
      if (typeof v === 'string')
        message = v
      else if (typeof v === 'number')
        status = v
    })
    this.res.setHeader('content-type', 'text/plain')
    this.res.statusCode = status
    this.res.end(message)
  },

  // Call this in your initialize function to enforce
  // a maximum request size. Useful for file uploads.
  enforceMaxLength: function(maxLen) {
    // respond with errors if max length is exceeded
    if (typeof maxLen === 'number') {
      var cl = req.headers['content-length']
      res.setHeader('max-length', ''+maxLen)
      if (!cl) {
        res.error(411)  // length required
        return false
      }
      if (cl > req.maxLen) {
        res.error(413) // too long
        return false
      }
    }
    return true
  },

  // Transfers this request over to another controller
  transfer: function(controller, action, data) {
    // make sure the page can no longer render from this controller
    this._render = function(){}

    var self = this
    // use nextTick to let the stack clear before firing the event
    // since it's possible the handler won't be registered yet.
    process.nextTick(function(){
      // the app will be listening for a transfer event and
      // will create a new controller based on the current one
      self.emit('transfer', self, controller, action, data)
    })
  }

})

Controller.extend = extendable
Plugin.makePluggable(Controller)
<|endoftext|>


<commit_before>/*eslint no-console: 0*/
/* global __DEVELOPMENT__ */
import 'babel-polyfill'
import Compression from 'compression'
import Express from 'express'
import Html from './helpers/Html'
import PrettyError from 'pretty-error'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import config from '../config'
import configureStore from './store/configure-store'
import cookieParser from 'cookie-parser'
import createRoutes, { ACTIVATE_PAGE_PATH, NO_CACHE_PAGES } from './routes/index'
import get from 'lodash/get'
import http from 'http'
import path from 'path'
import { NotFoundError } from './custom-error'
import { Provider } from 'react-redux'
import { RouterContext, match, createMemoryHistory } from 'react-router'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'
import { configureAction, authUserAction } from '@twreporter/registration'
import { syncHistoryWithStore } from 'react-router-redux'

/**
 * Define isomorphic constants.
 */
global.__CLIENT__ = false
global.__SERVER__ = true
global.__DEVELOPMENT__ = process.env.NODE_ENV !== 'production'

let webpackAssets = {
  javascripts: {
    main: config.webpackPublicPath + config.webpackOutputFilename,
    chunks: []
  },
  stylesheets: []
}

if (!__DEVELOPMENT__) {
  webpackAssets = require('../webpack-assets.json')
}

const app = new Express()
const server = new http.Server(app)
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')
app.use(Compression())
app.use(cookieParser())

const oneDay = 86400000
app.use('/asset', Express.static(path.join(__dirname, '../static/asset'), { maxAge: oneDay * 7 }))
app.use('/dist', Express.static(path.join(__dirname, '../dist'), { maxAge: oneDay * 30 }))
app.use('/meta', Express.static(path.join(__dirname, '../static/meta'), { maxAge: oneDay }))

app.use(function (req, res, next) {
  res.header('Access-Control-Allow-Origin', 'https://www.twreporter.org/')
  res.header('Access-Control-Allow-Headers', 'X-Requested-With')
  next()
})

// Add max-age: 900 on response header of /sw.js
app.get('/sw.js', Express.static(path.join(__dirname, '..'), { maxAge: 900 }))

app.get('/robots.txt', (req, res) => {
  res.format({
    'text/plain': function () {
      res.status(200).render('robots')
    }
  })
})

app.get('/check', (req, res) => {
  res.status(200)
  res.end('server is running')
})

const selectResponseStatus = (errorType) => {
  switch(errorType) {
    case '404':
      return 404
    case '500':
      return 500
    default:
      return 200
  }
}

app.get('*', async function (req, res, next) {
  const memoryHistory = createMemoryHistory(req.originalUrl)
  let store
  try {
    store = await configureStore(memoryHistory)
  } catch(err) {
    next(err)
  }
  const history = syncHistoryWithStore(memoryHistory, store)
  const path = get(req, 'path' , '')
  const routes = createRoutes(history)

  if (path === `/${ACTIVATE_PAGE_PATH}`) {
    // The following procedure is for OAuth (Google/Facebook)
    // setup token to redux state from cookies
    const authInfoString = get(req, 'cookies.auth_info', '')
    const authType = get(req, 'query.login', 'email signin')
    if (authInfoString) {
      const authInfoObj = JSON.parse(authInfoString)
      const jwt = get(authInfoObj, 'jwt', '')
      if (jwt) {
        store.dispatch(authUserAction(authType, authInfoObj))
      }
    }
  }
  // setup authentication api server url and endpoints
  store.dispatch(configureAction(config.registrationConfigure))

  match({ history, routes, location: req.originalUrl }, (error, redirectLocation, renderProps) => {
    /*
      What's in renderProps: {
        matchContext,
        router,
        ...routerState
      } https://github.com/ReactTraining/react-router/blob/v3/docs/Glossary.md#routerstate
    */
    if (redirectLocation) {
      res.redirect(301, redirectLocation.pathname + redirectLocation.search)
    } else if (error) {
      throw new Error(error)
    } else if (!renderProps) {
      throw new NotFoundError()
    } else {
      const getReduxPromise = function () {
        const query = get(renderProps, 'location.query', {})
        const params = get(renderProps, 'params', {})
        const pathname = get(renderProps, 'location.pathname', '')

        let comp = renderProps.components[renderProps.components.length - 1]
        comp = comp.WrappedComponent || comp
        const promise = comp.fetchData ?
          comp.fetchData({ query, params, store, history, pathname }) :
          Promise.resolve()

        return promise
      }

      getReduxPromise().then(() => {
        const sheet = new ServerStyleSheet()
        const content = ReactDOMServer.renderToString(
          <Provider store={store} >
            <StyleSheetManager sheet={sheet.instance}>
              { <RouterContext {...renderProps} /> }
            </StyleSheetManager>
          </Provider>
          )

        // set Cache-Control header for caching
        if (!res.headersSent) {
          const pathname = get(renderProps, 'location.pathname', '')
          // set Cache-Control directive in response header
          for(const noCachePage of NO_CACHE_PAGES) {
            if (pathname.indexOf(noCachePage) > -1) {
              res.header('Cache-Control', 'no-store')
              break
            } else {
              res.header('Cache-Control', 'public, max-age=300')
            }
          }
        }

        const html = ReactDOMServer.renderToStaticMarkup(
            <Html
              content={content}
              store={store}
              assets={webpackAssets}
              styleElement={sheet.getStyleElement()}
            />
          )
        res.status(selectResponseStatus(get(renderProps, 'params.errorType')))
        res.send(`<!doctype html>${html}`)
      })
      .catch((err) => {
        next(err)
      })
    }
  })
})

//
// Error handling
//
const pe = new PrettyError()
pe.skipNodeFiles()
pe.skipPackage('express')

app.use((err, req, res, next) => { // eslint-disable-line no-unused-vars
  console.log(pe.render(err)) // eslint-disable-line no-console
  if (err instanceof NotFoundError || get(err, 'response.status') === 404) {
    res.redirect('/error/404')
  } else {
    res.redirect('/error/500')
  }
})

if (config.port) {
  server.listen(config.port, (err) => {
    if (err) {
      console.error(err)
    }
    console.info('==> 💻  Open http://%s:%s in a browser to view the app.', config.host, config.port)
  })
} else {
  console.error('==>     ERROR: No PORT environment variable has been specified')
}

module.exports = server
<commit_msg>[Bug] Fix server error - unhandle promise rejection<commit_after>/*eslint no-console: 0*/
/* global __DEVELOPMENT__ */
import 'babel-polyfill'
import Compression from 'compression'
import Express from 'express'
import Html from './helpers/Html'
import PrettyError from 'pretty-error'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import config from '../config'
import configureStore from './store/configure-store'
import cookieParser from 'cookie-parser'
import createRoutes, { ACTIVATE_PAGE_PATH, NO_CACHE_PAGES } from './routes/index'
import get from 'lodash/get'
import http from 'http'
import path from 'path'
import { NotFoundError } from './custom-error'
import { Provider } from 'react-redux'
import { RouterContext, match, createMemoryHistory } from 'react-router'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'
import { configureAction, authUserAction } from '@twreporter/registration'
import { syncHistoryWithStore } from 'react-router-redux'

/**
 * Define isomorphic constants.
 */
global.__CLIENT__ = false
global.__SERVER__ = true
global.__DEVELOPMENT__ = process.env.NODE_ENV !== 'production'

let webpackAssets = {
  javascripts: {
    main: config.webpackPublicPath + config.webpackOutputFilename,
    chunks: []
  },
  stylesheets: []
}

if (!__DEVELOPMENT__) {
  webpackAssets = require('../webpack-assets.json')
}

const app = new Express()
const server = new http.Server(app)
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')
app.use(Compression())
app.use(cookieParser())

const oneDay = 86400000
app.use('/asset', Express.static(path.join(__dirname, '../static/asset'), { maxAge: oneDay * 7 }))
app.use('/dist', Express.static(path.join(__dirname, '../dist'), { maxAge: oneDay * 30 }))
app.use('/meta', Express.static(path.join(__dirname, '../static/meta'), { maxAge: oneDay }))

app.use(function (req, res, next) {
  res.header('Access-Control-Allow-Origin', 'https://www.twreporter.org/')
  res.header('Access-Control-Allow-Headers', 'X-Requested-With')
  next()
})

// Add max-age: 900 on response header of /sw.js
app.get('/sw.js', Express.static(path.join(__dirname, '..'), { maxAge: 900 }))

app.get('/robots.txt', (req, res) => {
  res.format({
    'text/plain': function () {
      res.status(200).render('robots')
    }
  })
})

app.get('/check', (req, res) => {
  res.status(200)
  res.end('server is running')
})

const selectResponseStatus = (errorType) => {
  switch(errorType) {
    case '404':
      return 404
    case '500':
      return 500
    default:
      return 200
  }
}

app.get('*', async function (req, res, next) {
  const memoryHistory = createMemoryHistory(req.originalUrl)
  let store
  try {
    store = await configureStore(memoryHistory)
  } catch(err) {
    next(err)
  }
  const history = syncHistoryWithStore(memoryHistory, store)
  const path = get(req, 'path' , '')
  const routes = createRoutes(history)

  if (path === `/${ACTIVATE_PAGE_PATH}`) {
    // The following procedure is for OAuth (Google/Facebook)
    // setup token to redux state from cookies
    const authInfoString = get(req, 'cookies.auth_info', '')
    const authType = get(req, 'query.login', 'email signin')
    if (authInfoString) {
      const authInfoObj = JSON.parse(authInfoString)
      const jwt = get(authInfoObj, 'jwt', '')
      if (jwt) {
        store.dispatch(authUserAction(authType, authInfoObj))
      }
    }
  }
  // setup authentication api server url and endpoints
  store.dispatch(configureAction(config.registrationConfigure))

  match({ history, routes, location: req.originalUrl }, (error, redirectLocation, renderProps) => {
    /*
      What's in renderProps: {
        matchContext,
        router,
        ...routerState
      } https://github.com/ReactTraining/react-router/blob/v3/docs/Glossary.md#routerstate
    */
    if (redirectLocation) {
      res.redirect(301, redirectLocation.pathname + redirectLocation.search)
    } else if (error) {
      return next(new Error(error))
    } else if (!renderProps) {
      return next(new NotFoundError())
    } else {
      const getReduxPromise = function () {
        const query = get(renderProps, 'location.query', {})
        const params = get(renderProps, 'params', {})
        const pathname = get(renderProps, 'location.pathname', '')

        let comp = renderProps.components[renderProps.components.length - 1]
        comp = comp.WrappedComponent || comp
        const promise = comp.fetchData ?
          comp.fetchData({ query, params, store, history, pathname }) :
          Promise.resolve()

        return promise
      }

      getReduxPromise().then(() => {
        const sheet = new ServerStyleSheet()
        const content = ReactDOMServer.renderToString(
          <Provider store={store} >
            <StyleSheetManager sheet={sheet.instance}>
              { <RouterContext {...renderProps} /> }
            </StyleSheetManager>
          </Provider>
          )

        // set Cache-Control header for caching
        if (!res.headersSent) {
          const pathname = get(renderProps, 'location.pathname', '')
          // set Cache-Control directive in response header
          for(const noCachePage of NO_CACHE_PAGES) {
            if (pathname.indexOf(noCachePage) > -1) {
              res.header('Cache-Control', 'no-store')
              break
            } else {
              res.header('Cache-Control', 'public, max-age=300')
            }
          }
        }

        const html = ReactDOMServer.renderToStaticMarkup(
            <Html
              content={content}
              store={store}
              assets={webpackAssets}
              styleElement={sheet.getStyleElement()}
            />
          )
        res.status(selectResponseStatus(get(renderProps, 'params.errorType')))
        res.send(`<!doctype html>${html}`)
      })
      .catch((err) => {
        next(err)
      })
    }
  })
})

//
// Error handling
//
const pe = new PrettyError()
pe.skipNodeFiles()
pe.skipPackage('express')

app.use((err, req, res, next) => { // eslint-disable-line no-unused-vars
  console.log(pe.render(err)) // eslint-disable-line no-console
  if (err instanceof NotFoundError || get(err, 'response.status') === 404) {
    res.redirect('/error/404')
  } else {
    res.redirect('/error/500')
  }
})

if (config.port) {
  server.listen(config.port, (err) => {
    if (err) {
      console.error(err)
    }
    console.info('==> 💻  Open http://%s:%s in a browser to view the app.', config.host, config.port)
  })
} else {
  console.error('==>     ERROR: No PORT environment variable has been specified')
}

module.exports = server
<|endoftext|>