<commit_before>//----------------------------------------------------------------------------
//  Copyright (C) 2013 The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

//============================================================================
// Base Widget Model and View classes
//============================================================================

/**
 * @module IPython
 * @namespace IPython
 **/

define(["notebook/js/widgetmanager",
        "underscore",
        "backbone"],
function(WidgetManager, Underscore, Backbone){

    var WidgetModel = Backbone.Model.extend({
        constructor: function (widget_manager, model_id, comm) {
            // Construcctor
            //
            // Creates a WidgetModel instance.
            //
            // Parameters
            // ----------
            // widget_manager : WidgetManager instance
            // model_id : string
            //      An ID unique to this model.
            // comm : Comm instance (optional)
            this.widget_manager = widget_manager;
            this.pending_msgs = 0;
            this.msg_throttle = 2;
            this.msg_buffer = null;
            this.key_value_lock = null;
            this.id = model_id;
            this.views = [];

            if (comm !== undefined) {
                // Remember comm associated with the model.
                this.comm = comm;
                comm.model = this;

                // Hook comm messages up to model.
                comm.on_close($.proxy(this._handle_comm_closed, this));
                comm.on_msg($.proxy(this._handle_comm_msg, this));
            }
            return Backbone.Model.apply(this);
        },

        send: function (content, callbacks) {
            // Send a custom msg over the comm.
            if (this.comm !== undefined) {
                var data = {method: 'custom', content: content};
                this.comm.send(data, callbacks);
            }
        },

        _handle_comm_closed: function (msg) {
            // Handle when a widget is closed.
            this.trigger('comm:close');
            delete this.comm.model; // Delete ref so GC will collect widget model.
            delete this.comm;
            delete this.model_id; // Delete id from model so widget manager cleans up.
            // TODO: Handle deletion, like this.destroy(), and delete views, etc.
        },

        _handle_comm_msg: function (msg) {
            // Handle incoming comm msg.
            var method = msg.content.data.method;
            switch (method) {
                case 'update':
                    this.apply_update(msg.content.data.state);
                    break;
                case 'custom':
                    this.trigger('msg:custom', msg.content.data.content);
                    break;
                case 'display':
                    this.widget_manager.display_view(msg, this);
                    break;
            }
        },

        apply_update: function (state) {
            // Handle when a widget is updated via the python side.
            _.each(state, function(value, key) {
                this.key_value_lock = [key, value];
                try {
                    this.set(key, this._unpack_models(value));
                } finally {
                    this.key_value_lock = null;
                }
            });
        },

        _handle_status: function (msg, callbacks) {
            // Handle status msgs.

            // execution_state : ('busy', 'idle', 'starting')
            if (this.comm !== undefined) {
                if (msg.content.execution_state ==='idle') {
                    // Send buffer if this message caused another message to be
                    // throttled.
                    if (this.msg_buffer !== null &&
                        this.msg_throttle === this.pending_msgs) {
                        var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
                        this.comm.send(data, callbacks);
                        this.msg_buffer = null;
                    } else {
                        --this.pending_msgs;
                    }
                }
            }
        },

        callbacks: function(view) {
            // Create msg callbacks for a comm msg.
            var callbacks = this.widget_manager.callbacks(view);

            if (callbacks.iopub === undefined) {
                callbacks.iopub = {};
            }

            var that = this;
            callbacks.iopub.status = function (msg) {
                that._handle_status(msg, callbacks);
            }
            return callbacks;
        },

        sync: function (method, model, options) {

            // Make sure a comm exists.
            var error = options.error || function() {
                console.error('Backbone sync error:', arguments);
            }
            if (this.comm === undefined) {
                error();
                return false;
            }

            // Delete any key value pairs that the back-end already knows about.
            var attrs = (method === 'patch') ? options.attrs : model.toJSON(options);
            if (this.key_value_lock !== null) {
                var key = this.key_value_lock[0];
                var value = this.key_value_lock[1];
                if (attrs[key] === value) {
                    delete attrs[key];
                }
            }

            // Only sync if there are attributes to send to the back-end.
            if (attr.length > 0) {
                var callbacks = options.callbacks || {};
                if (this.pending_msgs >= this.msg_throttle) {
                    // The throttle has been exceeded, buffer the current msg so
                    // it can be sent once the kernel has finished processing
                    // some of the existing messages.

                    // Combine updates if it is a 'patch' sync, otherwise replace updates
                    switch (method) {
                        case 'patch':
                            this.msg_buffer = $.extend(this.msg_buffer || {}, attrs);
                            break;
                        case 'update':
                        case 'create':
                            this.msg_buffer = attrs;
                            break;
                        default:
                            error();
                            return false;
                    }
                    this.msg_buffer_callbacks = callbacks;

                } else {
                    // We haven't exceeded the throttle, send the message like
                    // normal.  If this is a patch operation, just send the
                    // changes.
                    var data = {method: 'backbone', sync_data: attrs};
                    this.comm.send(data, callbacks);
                    this.pending_msgs++;
                }
            }
            // Since the comm is a one-way communication, assume the message
            // arrived.  Don't call success since we don't have a model back from the server
            // this means we miss out on the 'sync' event.
        },

        save_changes: function(callbacks) {
            // Push this model's state to the back-end
            //
            // This invokes a Backbone.Sync.
            this.save(this.changedAttributes(), {patch: true, callbacks: callbacks});
        },

        _pack_models: function(value) {
            // Replace models with model ids recursively.
            if (value instanceof Backbone.Model) {
                return value.id;
            } else if (value instanceof Object) {
                var packed = {};
                _.each(value, function(sub_value, key) {
                    packed[key] = this._pack_models(sub_value);
                });
                return packed;
            } else {
                return value;
            }
        },

        _unpack_models: function(value) {
            // Replace model ids with models recursively.
            if (value instanceof Object) {
                var unpacked = {};
                _.each(value, function(sub_value, key) {
                    unpacked[key] = this._unpack_models(sub_value);
                });
                return unpacked;
            } else {
                var model = this.widget_manager.get_model(value);
                if (model !== null) {
                    return model;
                } else {
                    return value;
                }
            }
        },

    });
    WidgetManager.register_widget_model('WidgetModel', WidgetModel);

    var WidgetView = Backbone.View.extend({
        initialize: function(parameters) {
            // Public constructor.
            this.model.on('change',this.update,this);
            this.options = parameters.options;
            this.child_views = [];
            this.model.views.push(this);
        },

        update: function(){
            // Triggered on model change.
            //
            // Update view to be consistent with this.model
        },

        create_child_view: function(child_model, options) {
            // Create and return a child view.
            //
            // -given a model and (optionally) a view name if the view name is
            // not given, it defaults to the model's default view attribute.

            // TODO: this is hacky, and makes the view depend on this cell attribute and widget manager behavior
            // it would be great to have the widget manager add the cell metadata
            // to the subview without having to add it here.
            options = options || {};
            options.cell = this.options.cell;
            var child_view = this.model.widget_manager.create_view(child_model, options);
            this.child_views[child_model.id] = child_view;
            return child_view;
        },

        delete_child_view: function(child_model, options) {
            // Delete a child view that was previously created using create_child_view.
            var view = this.child_views[child_model.id];
            delete this.child_views[child_model.id];
            view.remove();
        },

        do_diff: function(old_list, new_list, removed_callback, added_callback) {
            // Difference a changed list and call remove and add callbacks for
            // each removed and added item in the new list.
            //
            // Parameters
            // ----------
            // old_list : array
            // new_list : array
            // removed_callback : Callback(item)
            //      Callback that is called for each item removed.
            // added_callback : Callback(item)
            //      Callback that is called for each item added.

            // removed items
            _.each(_.difference(old_list, new_list), function(item, index, list) {
                removed_callback(item);
            }, this);

            // added items
            _.each(_.difference(new_list, old_list), function(item, index, list) {
                added_callback(item);
            }, this);
        },

        callbacks: function(){
            // Create msg callbacks for a comm msg.
            return this.model.callbacks(this);
        },

        render: function(){
            // Render the view.
            //
            // By default, this is only called the first time the view is created
        },

        send: function (content) {
            // Send a custom msg associated with this view.
            this.model.send(content, this.callbacks());
        },

        touch: function () {
            this.model.save_changes(this.callbacks());
        },

    });

    var DOMWidgetView = WidgetView.extend({
        initialize: function (options) {
            // Public constructor

            // In the future we may want to make changes more granular
            // (e.g., trigger on visible:change).
            this.model.on('change', this.update, this);
            this.model.on('msg:custom', this.on_msg, this);
            DOMWidgetView.__super__.initialize.apply(this, arguments);
        },

        on_msg: function(msg) {
            // Handle DOM specific msgs.
            switch(msg.msg_type) {
                case 'add_class':
                    this.add_class(msg.selector, msg.class_list);
                    break;
                case 'remove_class':
                    this.remove_class(msg.selector, msg.class_list);
                    break;
            }
        },

        add_class: function (selector, class_list) {
            // Add a DOM class to an element.
            this._get_selector_element(selector).addClass(class_list);
        },

        remove_class: function (selector, class_list) {
            // Remove a DOM class from an element.
            this._get_selector_element(selector).removeClass(class_list);
        },

        update: function () {
            // Update the contents of this view
            //
            // Called when the model is changed.  The model may have been
            // changed by another view or by a state update from the back-end.
            //      The very first update seems to happen before the element is
            // finished rendering so we use setTimeout to give the element time
            // to render
            var e = this.$el;
            var visible = this.model.get('visible');
            setTimeout(function() {e.toggle(visible)},0);

            var css = this.model.get('_css');
            if (css === undefined) {return;}
            _.each(css, function(css_traits, selector){
                // Apply the css traits to all elements that match the selector.
                var elements = this._get_selector_element(selector);
                if (elements.length > 0) {
                    _.each(css_traits, function(css_value, css_key){
                        elements.css(css_key, css_value);
                    });
                }
            });

        },

        _get_selector_element: function (selector) {
            // Get the elements via the css selector.

            // If the selector is blank, apply the style to the $el_to_style
            // element.  If the $el_to_style element is not defined, use apply
            // the style to the view's element.
            var elements;
            if (selector === undefined || selector === null || selector === '') {
                if (this.$el_to_style === undefined) {
                    elements = this.$el;
                } else {
                    elements = this.$el_to_style;
                }
            } else {
                elements = this.$el.find(selector);
            }
            return elements;
        },
    });

    IPython.WidgetModel = WidgetModel;
    IPython.WidgetView = WidgetView;
    IPython.DOMWidgetView = DOMWidgetView;

    // Pass through WidgetManager namespace.
    return WidgetManager;
});
<commit_msg>- remove second line in create_child_view - add undefined check in delete_child_view<commit_after>//----------------------------------------------------------------------------
//  Copyright (C) 2013 The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

//============================================================================
// Base Widget Model and View classes
//============================================================================

/**
 * @module IPython
 * @namespace IPython
 **/

define(["notebook/js/widgetmanager",
        "underscore",
        "backbone"],
function(WidgetManager, Underscore, Backbone){

    var WidgetModel = Backbone.Model.extend({
        constructor: function (widget_manager, model_id, comm) {
            // Construcctor
            //
            // Creates a WidgetModel instance.
            //
            // Parameters
            // ----------
            // widget_manager : WidgetManager instance
            // model_id : string
            //      An ID unique to this model.
            // comm : Comm instance (optional)
            this.widget_manager = widget_manager;
            this.pending_msgs = 0;
            this.msg_throttle = 2;
            this.msg_buffer = null;
            this.key_value_lock = null;
            this.id = model_id;
            this.views = [];

            if (comm !== undefined) {
                // Remember comm associated with the model.
                this.comm = comm;
                comm.model = this;

                // Hook comm messages up to model.
                comm.on_close($.proxy(this._handle_comm_closed, this));
                comm.on_msg($.proxy(this._handle_comm_msg, this));
            }
            return Backbone.Model.apply(this);
        },

        send: function (content, callbacks) {
            // Send a custom msg over the comm.
            if (this.comm !== undefined) {
                var data = {method: 'custom', content: content};
                this.comm.send(data, callbacks);
            }
        },

        _handle_comm_closed: function (msg) {
            // Handle when a widget is closed.
            this.trigger('comm:close');
            delete this.comm.model; // Delete ref so GC will collect widget model.
            delete this.comm;
            delete this.model_id; // Delete id from model so widget manager cleans up.
            // TODO: Handle deletion, like this.destroy(), and delete views, etc.
        },

        _handle_comm_msg: function (msg) {
            // Handle incoming comm msg.
            var method = msg.content.data.method;
            switch (method) {
                case 'update':
                    this.apply_update(msg.content.data.state);
                    break;
                case 'custom':
                    this.trigger('msg:custom', msg.content.data.content);
                    break;
                case 'display':
                    this.widget_manager.display_view(msg, this);
                    break;
            }
        },

        apply_update: function (state) {
            // Handle when a widget is updated via the python side.
            _.each(state, function(value, key) {
                this.key_value_lock = [key, value];
                try {
                    this.set(key, this._unpack_models(value));
                } finally {
                    this.key_value_lock = null;
                }
            });
        },

        _handle_status: function (msg, callbacks) {
            // Handle status msgs.

            // execution_state : ('busy', 'idle', 'starting')
            if (this.comm !== undefined) {
                if (msg.content.execution_state ==='idle') {
                    // Send buffer if this message caused another message to be
                    // throttled.
                    if (this.msg_buffer !== null &&
                        this.msg_throttle === this.pending_msgs) {
                        var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
                        this.comm.send(data, callbacks);
                        this.msg_buffer = null;
                    } else {
                        --this.pending_msgs;
                    }
                }
            }
        },

        callbacks: function(view) {
            // Create msg callbacks for a comm msg.
            var callbacks = this.widget_manager.callbacks(view);

            if (callbacks.iopub === undefined) {
                callbacks.iopub = {};
            }

            var that = this;
            callbacks.iopub.status = function (msg) {
                that._handle_status(msg, callbacks);
            }
            return callbacks;
        },

        sync: function (method, model, options) {

            // Make sure a comm exists.
            var error = options.error || function() {
                console.error('Backbone sync error:', arguments);
            }
            if (this.comm === undefined) {
                error();
                return false;
            }

            // Delete any key value pairs that the back-end already knows about.
            var attrs = (method === 'patch') ? options.attrs : model.toJSON(options);
            if (this.key_value_lock !== null) {
                var key = this.key_value_lock[0];
                var value = this.key_value_lock[1];
                if (attrs[key] === value) {
                    delete attrs[key];
                }
            }

            // Only sync if there are attributes to send to the back-end.
            if (attr.length > 0) {
                var callbacks = options.callbacks || {};
                if (this.pending_msgs >= this.msg_throttle) {
                    // The throttle has been exceeded, buffer the current msg so
                    // it can be sent once the kernel has finished processing
                    // some of the existing messages.

                    // Combine updates if it is a 'patch' sync, otherwise replace updates
                    switch (method) {
                        case 'patch':
                            this.msg_buffer = $.extend(this.msg_buffer || {}, attrs);
                            break;
                        case 'update':
                        case 'create':
                            this.msg_buffer = attrs;
                            break;
                        default:
                            error();
                            return false;
                    }
                    this.msg_buffer_callbacks = callbacks;

                } else {
                    // We haven't exceeded the throttle, send the message like
                    // normal.  If this is a patch operation, just send the
                    // changes.
                    var data = {method: 'backbone', sync_data: attrs};
                    this.comm.send(data, callbacks);
                    this.pending_msgs++;
                }
            }
            // Since the comm is a one-way communication, assume the message
            // arrived.  Don't call success since we don't have a model back from the server
            // this means we miss out on the 'sync' event.
        },

        save_changes: function(callbacks) {
            // Push this model's state to the back-end
            //
            // This invokes a Backbone.Sync.
            this.save(this.changedAttributes(), {patch: true, callbacks: callbacks});
        },

        _pack_models: function(value) {
            // Replace models with model ids recursively.
            if (value instanceof Backbone.Model) {
                return value.id;
            } else if (value instanceof Object) {
                var packed = {};
                _.each(value, function(sub_value, key) {
                    packed[key] = this._pack_models(sub_value);
                });
                return packed;
            } else {
                return value;
            }
        },

        _unpack_models: function(value) {
            // Replace model ids with models recursively.
            if (value instanceof Object) {
                var unpacked = {};
                _.each(value, function(sub_value, key) {
                    unpacked[key] = this._unpack_models(sub_value);
                });
                return unpacked;
            } else {
                var model = this.widget_manager.get_model(value);
                if (model !== null) {
                    return model;
                } else {
                    return value;
                }
            }
        },

    });
    WidgetManager.register_widget_model('WidgetModel', WidgetModel);

    var WidgetView = Backbone.View.extend({
        initialize: function(parameters) {
            // Public constructor.
            this.model.on('change',this.update,this);
            this.options = parameters.options;
            this.child_views = [];
            this.model.views.push(this);
        },

        update: function(){
            // Triggered on model change.
            //
            // Update view to be consistent with this.model
        },

        create_child_view: function(child_model, options) {
            // Create and return a child view.
            //
            // -given a model and (optionally) a view name if the view name is
            // not given, it defaults to the model's default view attribute.

            // TODO: this is hacky, and makes the view depend on this cell attribute and widget manager behavior
            // it would be great to have the widget manager add the cell metadata
            // to the subview without having to add it here.
            var child_view = this.model.widget_manager.create_view(child_model, options || {});
            this.child_views[child_model.id] = child_view;
            return child_view;
        },

        delete_child_view: function(child_model, options) {
            // Delete a child view that was previously created using create_child_view.
            var view = this.child_views[child_model.id];
            if (view !== undefined) {
                delete this.child_views[child_model.id];
                view.remove();
            }
        },

        do_diff: function(old_list, new_list, removed_callback, added_callback) {
            // Difference a changed list and call remove and add callbacks for
            // each removed and added item in the new list.
            //
            // Parameters
            // ----------
            // old_list : array
            // new_list : array
            // removed_callback : Callback(item)
            //      Callback that is called for each item removed.
            // added_callback : Callback(item)
            //      Callback that is called for each item added.

            // removed items
            _.each(_.difference(old_list, new_list), function(item, index, list) {
                removed_callback(item);
            }, this);

            // added items
            _.each(_.difference(new_list, old_list), function(item, index, list) {
                added_callback(item);
            }, this);
        },

        callbacks: function(){
            // Create msg callbacks for a comm msg.
            return this.model.callbacks(this);
        },

        render: function(){
            // Render the view.
            //
            // By default, this is only called the first time the view is created
        },

        send: function (content) {
            // Send a custom msg associated with this view.
            this.model.send(content, this.callbacks());
        },

        touch: function () {
            this.model.save_changes(this.callbacks());
        },

    });

    var DOMWidgetView = WidgetView.extend({
        initialize: function (options) {
            // Public constructor

            // In the future we may want to make changes more granular
            // (e.g., trigger on visible:change).
            this.model.on('change', this.update, this);
            this.model.on('msg:custom', this.on_msg, this);
            DOMWidgetView.__super__.initialize.apply(this, arguments);
        },

        on_msg: function(msg) {
            // Handle DOM specific msgs.
            switch(msg.msg_type) {
                case 'add_class':
                    this.add_class(msg.selector, msg.class_list);
                    break;
                case 'remove_class':
                    this.remove_class(msg.selector, msg.class_list);
                    break;
            }
        },

        add_class: function (selector, class_list) {
            // Add a DOM class to an element.
            this._get_selector_element(selector).addClass(class_list);
        },

        remove_class: function (selector, class_list) {
            // Remove a DOM class from an element.
            this._get_selector_element(selector).removeClass(class_list);
        },

        update: function () {
            // Update the contents of this view
            //
            // Called when the model is changed.  The model may have been
            // changed by another view or by a state update from the back-end.
            //      The very first update seems to happen before the element is
            // finished rendering so we use setTimeout to give the element time
            // to render
            var e = this.$el;
            var visible = this.model.get('visible');
            setTimeout(function() {e.toggle(visible)},0);

            var css = this.model.get('_css');
            if (css === undefined) {return;}
            _.each(css, function(css_traits, selector){
                // Apply the css traits to all elements that match the selector.
                var elements = this._get_selector_element(selector);
                if (elements.length > 0) {
                    _.each(css_traits, function(css_value, css_key){
                        elements.css(css_key, css_value);
                    });
                }
            });

        },

        _get_selector_element: function (selector) {
            // Get the elements via the css selector.

            // If the selector is blank, apply the style to the $el_to_style
            // element.  If the $el_to_style element is not defined, use apply
            // the style to the view's element.
            var elements;
            if (selector === undefined || selector === null || selector === '') {
                if (this.$el_to_style === undefined) {
                    elements = this.$el;
                } else {
                    elements = this.$el_to_style;
                }
            } else {
                elements = this.$el.find(selector);
            }
            return elements;
        },
    });

    IPython.WidgetModel = WidgetModel;
    IPython.WidgetView = WidgetView;
    IPython.DOMWidgetView = DOMWidgetView;

    // Pass through WidgetManager namespace.
    return WidgetManager;
});
<|endoftext|>


<commit_before>(function (Q, $) {
/**
 * @module Q-tools
 */

/**
 * Implements lazy-loading for various types of elements.
 * By default, has implementations for "img" and "Q_tool" selectors.
 * The "Q_tool" handler only works with tools whose elements have a "data-q-lazyload" attribute .
 * Note that the elements must have a stable, nonzero width height set in the CSS even
 * if they are empty, otherwise they might not be lazy-loaded, and they might
 * thrash back and forth if removed.
 * @class Q lazyload
 * @constructor
 * @param {Object} [options] Override various options for this tool
 * @param {Array} [options.handle] Set this to
 * @param {Object} [options.handlers] You can modify the defaults by passing an object
 *    that looks like {handlerName: { selector: String, entering: Function, exiting: Function, preparing: Function}}
 *    Function "entering" receives (element, intersectionObserverEntry)
 *    Function "exiting" receives (element, intersectionObserverEntry)
 *    Function "preparing" receives (element) and is used the first time to prepare the element
 *    Both functions must return true if the element was modified.
 * @param {Element} [root=tool.element] The container inside which to watch for intesections
 * @param {Object} [observerOptions] Override any options to pass to IntersectionObserver
 * @param {Element} [observerOptions.root=tool.element.scrollingParent(true)]
 * @param {String} [observerOptions.rootMargin='0px']
 * @param {String} [observerOptions.threshold=0]
 * @return {Q.Tool}
 */
Q.Tool.define('Q/lazyload', function (options) {

	var tool = this;
	var state = this.state;

	var Elp = Element.prototype;

	Q.ensure(window.IntersectionObserver, _polyfill, function () {
		// Observe whatever is on the page already
		tool.observer = _createObserver(tool, tool.element.scrollingParent(true));
		tool.observe(tool.prepare(tool.element, false));

		// Override innerHTML

		var originalSet = Object.getOwnPropertyDescriptor(Elp, 'innerHTML').set;
		var originalGet = Object.getOwnPropertyDescriptor(Elp, 'innerHTML').get;

		Object.defineProperty(Elp, 'innerHTML', {
			set: function (html) {
				var element = document.createElement('div');
				var root = state.root || document.documentElement;
				var inside = (root === this) || root.contains(this);
				if (!inside) {
					originalSet.call(this, html);
					return html;
				}
				originalSet.call(element, html);
				var found = false;
				Q.each(state.handlers, function (name, info) {
					var elements = element.querySelectorAll
						? Array.from(element.querySelectorAll(info.selector))
						: [];
					if (elements.length) {
						found = true;
						return false;
					}
				});
				if (found) {
					html = originalGet.call(element);
				}
				originalSet.call(this, html);
				if (found) {
					tool.observe(tool.prepare(this, true));
				}
				return html;
			},
			get: originalGet
		});

		Q.each(['insertBefore', 'appendChild'], function (i, fn) {
			var orig = Elp[fn];
			Elp[fn] = function (element) {
				if (!(element instanceof HTMLElement)) {
					return orig.apply(this, arguments);
				}
				var root = state.root || document.documentElement;
				var inside = (root === this) || root.contains(this);
				if (!inside) {
					return orig.apply(this, arguments);
				}
				var found = false;
				Q.each(state.handlers, function (name, info) {
					if (element.matches && element.matches(info.selector)) {
						found = true;
						return false;
					}
					var elements = element.querySelectorAll
						? Array.from(element.querySelectorAll(info.selector))
						: [];
					if (elements.length) {
						found = true;
						return false;
					}
				});
				if (found) {
					tool.observe(tool.prepare(element, true));
				}
				return orig.apply(this, arguments);
			};
		});
	});

},

{
	handlers: {
		img: {
			selector: 'img',
			entering: function (img, entry) {
				function _loaded() {
					img.addClass('Q_lazy_loaded');
				}
				var src = img.getAttribute('data-lazyload-src');
				if (src) {
					img.setAttribute('src', Q.url(src));
					img.removeAttribute('data-lazyload-src');
					img.addClass('Q_lazy_load');
					if (img.complete) {
						_loaded();
					}
					img.addEventListener('load', _loaded);
				}
				return true;
			},
			exiting: function (img) {
				// no need to do anything
				return true;
			},
			preparing: function (img, beingInsertedIntoDOM) {
				if (!beingInsertedIntoDOM) {
					return true; // too late anyway, browser will load image
				}
				if (img.hasClass('Q_lazy_load') || img.hasClass('Q_lazy_loaded')) {
					return true; // this was already processed by lazy-loading
				}
				var src = img.getAttribute('src');
				if (src && src.substr(0, 5) !== 'data:'
				&& !img.hasAttribute('data-lazyload-src')) {
					img.setAttribute('data-lazyload-src', Q.url(src));
					img.setAttribute('src', Q.url(
						Q.getObject('Q.images.lazyload.loadingSrc')
						|| "{{Q}}/img/throbbers/transparent.gif"
					));
				}
				return true;
			}
		},
		tool: {
			selector: '.Q_tool',
			entering: function (element, entry) {
				if (element.hasAttribute('data-q-lazyload')
				&& (!element.Q || !element.Q.tool)) {
					element.addClass('Q_lazy_load');
					element.setAttribute('data-q-lazyload', 'activating');
					Q.activate(element, function () {
						element.setAttribute('data-q-lazyload', 'activated');
						element.addClass('Q_lazy_loaded');
					}, {}, true);
					return true;
				}
			},
			exiting: function (element, entry) {
				if (element.hasAttribute('data-q-lazyload')
				&& element.Q.tool) {
					Q.Tool.remove(element);
					element.removeClass('Q_lazy_loading');
					element.removeClass('Q_lazy_loaded');
					element.setAttribute('data-q-lazyload', 'removed');
					if (this.state.handlers.tool.exitingRemoveHTML) {
						element.innerHTML = '';
					}
				}
				return true;
			},
			preparing: function (element) {
				return true;
			},
			exitingRemoveHTML: true
		}
	},
	root: undefined,
	observerOptions: {
		root: undefined,
		rootMargin: '0px',
		threshold: 0
	}
},

{
	prepare: function (container, beingInsertedIntoDOM) {
		var tool = this;
		var found = [];
		Q.each(tool.state.handlers, function (name, info) {
			var elements = container.querySelectorAll
				? Array.from(container.querySelectorAll(info.selector))
				: [];
			if (container.matches && container.matches(info.selector)) {
				elements.push(container);
			}
			Q.each(elements, function (i, element) {
				if (info.preparing.call(tool, element, beingInsertedIntoDOM) === true) {
					found.push(element);
				}
			});
		});
		return found;
	},
	observe: function (elements) {
		var tool = this;
		tool.observer && Q.each(elements, function (i, element) {
			tool.observer.observe(element);
		});
	},
	unobserve: function (elements) {
		var tool = this;
		tool.observer && Q.each(elements, function (i, element) {
			tool.observer.unobserve(element);
		});
	},
	Q: {
		beforeRemove: function () {
			this.observer && this.observer.disconnect();
		}
	}
});

function _createObserver(tool, container) {
	var o = Q.copy(tool.state.observerOptions);
	if (o.root === undefined) {
		o.root = (container === document.documentElement) ? null : (container || null);
	}
	return new IntersectionObserver(function (entries, observer) {
		Q.each(entries, function (i, entry) {
			Q.each(tool.state.handlers, function (name, info) {
				if (entry.target.matches && entry.target.matches(info.selector)) {
					if (entry.isIntersecting) {
						info.entering.call(tool, entry.target, entry);
					} else  {
						var rect = entry.target.getBoundingClientRect();
						if (rect.width > 0 && rect.height > 0) {
							// also covers !document.body.contains(entry.target)
							info.exiting.call(tool, entry.target, entry);
						} // otherwise it might get a false positive
					}
				}
			});
		});
	}, o);
}

function _polyfill(callback) {
	// Exit early if all IntersectionObserver and IntersectionObserverEntry
	// features are natively supported.
	if ('IntersectionObserver' in window &&
	    'IntersectionObserverEntry' in window &&
	    'intersectionRatio' in window.IntersectionObserverEntry.prototype) {

	  // Minimal polyfill for Edge 15's lack of `isIntersecting`
	  // See: https://github.com/w3c/IntersectionObserver/issues/211
	  if (!('isIntersecting' in window.IntersectionObserverEntry.prototype)) {
	    Object.defineProperty(window.IntersectionObserverEntry.prototype,
	      'isIntersecting', {
	      get: function () {
	        return this.intersectionRatio > 0;
	      }
	    });
	  }
	  return Q.handle(callback);
	}
	Q.addScript('{{Q}}/js/polyfills/IntersectionObserver.js', callback);
}

_polyfill();

})(Q, jQuery);<commit_msg>Made default for state.root in Q/lazyload<commit_after>(function (Q, $) {
/**
 * @module Q-tools
 */

/**
 * Implements lazy-loading for various types of elements.
 * By default, has implementations for "img" and "Q_tool" selectors.
 * The "Q_tool" handler only works with tools whose elements have a "data-q-lazyload" attribute .
 * Note that the elements must have a stable, nonzero width height set in the CSS even
 * if they are empty, otherwise they might not be lazy-loaded, and they might
 * thrash back and forth if removed.
 * @class Q lazyload
 * @constructor
 * @param {Object} [options] Override various options for this tool
 * @param {Array} [options.handle] Set this to
 * @param {Object} [options.handlers] You can modify the defaults by passing an object
 *    that looks like {handlerName: { selector: String, entering: Function, exiting: Function, preparing: Function}}
 *    Function "entering" receives (element, intersectionObserverEntry)
 *    Function "exiting" receives (element, intersectionObserverEntry)
 *    Function "preparing" receives (element) and is used the first time to prepare the element
 *    Both functions must return true if the element was modified.
 * @param {Element} [root=tool.element] The container inside which to watch for intesections
 * @param {Object} [observerOptions] Override any options to pass to IntersectionObserver
 * @param {Element} [observerOptions.root=tool.element.scrollingParent(true)]
 * @param {String} [observerOptions.rootMargin='0px']
 * @param {String} [observerOptions.threshold=0]
 * @return {Q.Tool}
 */
Q.Tool.define('Q/lazyload', function (options) {

	var tool = this;
	var state = this.state;
	state.root = state.root || this.element;

	var Elp = Element.prototype;

	Q.ensure(window.IntersectionObserver, _polyfill, function () {
		// Observe whatever is on the page already
		tool.observer = _createObserver(tool, tool.element.scrollingParent(true));
		tool.observe(tool.prepare(tool.element, false));

		// Override innerHTML

		var originalSet = Object.getOwnPropertyDescriptor(Elp, 'innerHTML').set;
		var originalGet = Object.getOwnPropertyDescriptor(Elp, 'innerHTML').get;

		Object.defineProperty(Elp, 'innerHTML', {
			set: function (html) {
				var element = document.createElement('div');
				var root = state.root || document.documentElement;
				var inside = (root === this) || root.contains(this);
				if (!inside) {
					originalSet.call(this, html);
					return html;
				}
				originalSet.call(element, html);
				var found = false;
				Q.each(state.handlers, function (name, info) {
					var elements = element.querySelectorAll
						? Array.from(element.querySelectorAll(info.selector))
						: [];
					if (elements.length) {
						found = true;
						return false;
					}
				});
				if (found) {
					html = originalGet.call(element);
				}
				originalSet.call(this, html);
				if (found) {
					tool.observe(tool.prepare(this, true));
				}
				return html;
			},
			get: originalGet
		});

		Q.each(['insertBefore', 'appendChild'], function (i, fn) {
			var orig = Elp[fn];
			Elp[fn] = function (element) {
				if (!(element instanceof HTMLElement)) {
					return orig.apply(this, arguments);
				}
				var root = state.root || document.documentElement;
				var inside = (root === this) || root.contains(this);
				if (!inside) {
					return orig.apply(this, arguments);
				}
				var found = false;
				Q.each(state.handlers, function (name, info) {
					if (element.matches && element.matches(info.selector)) {
						found = true;
						return false;
					}
					var elements = element.querySelectorAll
						? Array.from(element.querySelectorAll(info.selector))
						: [];
					if (elements.length) {
						found = true;
						return false;
					}
				});
				if (found) {
					tool.observe(tool.prepare(element, true));
				}
				return orig.apply(this, arguments);
			};
		});
	});

},

{
	handlers: {
		img: {
			selector: 'img',
			entering: function (img, entry) {
				function _loaded() {
					img.addClass('Q_lazy_loaded');
				}
				var src = img.getAttribute('data-lazyload-src');
				if (src) {
					img.setAttribute('src', Q.url(src));
					img.removeAttribute('data-lazyload-src');
					img.addClass('Q_lazy_load');
					if (img.complete) {
						_loaded();
					}
					img.addEventListener('load', _loaded);
				}
				return true;
			},
			exiting: function (img) {
				// no need to do anything
				return true;
			},
			preparing: function (img, beingInsertedIntoDOM) {
				if (!beingInsertedIntoDOM) {
					return true; // too late anyway, browser will load image
				}
				if (img.hasClass('Q_lazy_load') || img.hasClass('Q_lazy_loaded')) {
					return true; // this was already processed by lazy-loading
				}
				var src = img.getAttribute('src');
				if (src && src.substr(0, 5) !== 'data:'
				&& !img.hasAttribute('data-lazyload-src')) {
					img.setAttribute('data-lazyload-src', Q.url(src));
					img.setAttribute('src', Q.url(
						Q.getObject('Q.images.lazyload.loadingSrc')
						|| "{{Q}}/img/throbbers/transparent.gif"
					));
				}
				return true;
			}
		},
		tool: {
			selector: '.Q_tool',
			entering: function (element, entry) {
				if (element.hasAttribute('data-q-lazyload')
				&& (!element.Q || !element.Q.tool)) {
					element.addClass('Q_lazy_load');
					element.setAttribute('data-q-lazyload', 'activating');
					Q.activate(element, function () {
						element.setAttribute('data-q-lazyload', 'activated');
						element.addClass('Q_lazy_loaded');
					}, {}, true);
					return true;
				}
			},
			exiting: function (element, entry) {
				if (element.hasAttribute('data-q-lazyload')
				&& element.Q.tool) {
					Q.Tool.remove(element);
					element.removeClass('Q_lazy_loading');
					element.removeClass('Q_lazy_loaded');
					element.setAttribute('data-q-lazyload', 'removed');
					if (this.state.handlers.tool.exitingRemoveHTML) {
						element.innerHTML = '';
					}
				}
				return true;
			},
			preparing: function (element) {
				return true;
			},
			exitingRemoveHTML: true
		}
	},
	root: undefined,
	observerOptions: {
		root: undefined,
		rootMargin: '0px',
		threshold: 0
	}
},

{
	prepare: function (container, beingInsertedIntoDOM) {
		var tool = this;
		var found = [];
		Q.each(tool.state.handlers, function (name, info) {
			var elements = container.querySelectorAll
				? Array.from(container.querySelectorAll(info.selector))
				: [];
			if (container.matches && container.matches(info.selector)) {
				elements.push(container);
			}
			Q.each(elements, function (i, element) {
				if (info.preparing.call(tool, element, beingInsertedIntoDOM) === true) {
					found.push(element);
				}
			});
		});
		return found;
	},
	observe: function (elements) {
		var tool = this;
		tool.observer && Q.each(elements, function (i, element) {
			tool.observer.observe(element);
		});
	},
	unobserve: function (elements) {
		var tool = this;
		tool.observer && Q.each(elements, function (i, element) {
			tool.observer.unobserve(element);
		});
	},
	Q: {
		beforeRemove: function () {
			this.observer && this.observer.disconnect();
		}
	}
});

function _createObserver(tool, container) {
	var o = Q.copy(tool.state.observerOptions);
	if (o.root === undefined) {
		o.root = (container === document.documentElement) ? null : (container || null);
	}
	return new IntersectionObserver(function (entries, observer) {
		Q.each(entries, function (i, entry) {
			Q.each(tool.state.handlers, function (name, info) {
				if (entry.target.matches && entry.target.matches(info.selector)) {
					if (entry.isIntersecting) {
						info.entering.call(tool, entry.target, entry);
					} else  {
						var rect = entry.target.getBoundingClientRect();
						if (rect.width > 0 && rect.height > 0) {
							// also covers !document.body.contains(entry.target)
							info.exiting.call(tool, entry.target, entry);
						} // otherwise it might get a false positive
					}
				}
			});
		});
	}, o);
}

function _polyfill(callback) {
	// Exit early if all IntersectionObserver and IntersectionObserverEntry
	// features are natively supported.
	if ('IntersectionObserver' in window &&
	    'IntersectionObserverEntry' in window &&
	    'intersectionRatio' in window.IntersectionObserverEntry.prototype) {

	  // Minimal polyfill for Edge 15's lack of `isIntersecting`
	  // See: https://github.com/w3c/IntersectionObserver/issues/211
	  if (!('isIntersecting' in window.IntersectionObserverEntry.prototype)) {
	    Object.defineProperty(window.IntersectionObserverEntry.prototype,
	      'isIntersecting', {
	      get: function () {
	        return this.intersectionRatio > 0;
	      }
	    });
	  }
	  return Q.handle(callback);
	}
	Q.addScript('{{Q}}/js/polyfills/IntersectionObserver.js', callback);
}

_polyfill();

})(Q, jQuery);<|endoftext|>


<commit_before>/**
 * Documentation: http://docs.azk.io/Azkfile.js
 */
// Adds the systems that shape your system
systems({
  infoduca: {
    // Dependent systems
    depends: ["postgres"],
    // More images:  http://images.azk.io
    image: {"docker": "azukiapp/ruby:2.3.1"},
    // Steps to execute before running instances
    provision: [
      "bundle install --path /azk/bundler",
    ],
    workdir: "/azk/#{manifest.dir}",
    shell: "/bin/bash",
    command: ["bundle", "exec", "rackup", "config.ru", "--pid", "/tmp/ruby.pid", "--port", "$HTTP_PORT", "--host", "0.0.0.0"],
    wait: 20,
    mounts: {
      '/azk/#{manifest.dir}': sync("."),
      '/azk/bundler': persistent("./bundler"),
      '/azk/#{manifest.dir}/tmp': persistent("./tmp"),
      '/azk/#{manifest.dir}/log': path("./log"),
      '/azk/#{manifest.dir}/.bundle': path("./.bundle"),
    },
    scalable: {"default": 1},
    http: {
      domains: [ "#{system.name}.#{azk.default_domain}" ]
    },
    ports: {
      // exports global variables
      http: "3000/tcp",
    },
    envs: {
      // Make sure that the PORT value is the same as the one
      // in ports/http below, and that it's also the same
      // if you're setting it in a .env file
      RUBY_ENV: "development",
      BUNDLE_APP_CONFIG: "/azk/bundler",
    },
  },
  postgres: {
    // Dependent systems
    depends: [],
    // More images:  http://images.azk.io
    image: {"docker": "azukiapp/postgres:9.4"},
    shell: "/bin/bash",
    wait: 150,
    mounts: {
      '/var/lib/postgresql/data': persistent("postgres-data"),
      '/var/log/postgresql': persistent("postgres-log"),
    },
    ports: {
      // exports global variables
      data: "5432/tcp",
    },
    envs: {
      // set instances variables
      POSTGRES_USER: "azk",
      POSTGRES_PASS: "azk",
      POSTGRES_DB: "azk",
    },
    export_envs: {
      // exports variables for dependent systems
      DATABASE_URL: "postgres://#{envs.POSTGRES_USER}:#{envs.POSTGRES_PASS}@#{net.host}:#{net.port.data}/#{envs.POSTGRES_DATABASE}",
    },
  },
});
<commit_msg>[azk] Adding migration to provision<commit_after>/**
 * Documentation: http://docs.azk.io/Azkfile.js
 */
// Adds the systems that shape your system
systems({
  infoduca: {
    // Dependent systems
    depends: ["postgres"],
    // More images:  http://images.azk.io
    image: {"docker": "azukiapp/ruby:2.3.1"},
    // Steps to execute before running instances
    provision: [
      "bundle install --path /azk/bundler",
      "bundle exec rake db:migrate",
    ],
    workdir: "/azk/#{manifest.dir}",
    shell: "/bin/bash",
    command: ["bundle", "exec", "rackup", "config.ru", "--pid", "/tmp/ruby.pid", "--port", "$HTTP_PORT", "--host", "0.0.0.0"],
    wait: 20,
    mounts: {
      '/azk/#{manifest.dir}': sync("."),
      '/azk/bundler': persistent("./bundler"),
      '/azk/#{manifest.dir}/tmp': persistent("./tmp"),
      '/azk/#{manifest.dir}/log': path("./log"),
      '/azk/#{manifest.dir}/.bundle': path("./.bundle"),
    },
    scalable: {"default": 1},
    http: {
      domains: [ "#{system.name}.#{azk.default_domain}" ]
    },
    ports: {
      // exports global variables
      http: "3000/tcp",
    },
    envs: {
      // Make sure that the PORT value is the same as the one
      // in ports/http below, and that it's also the same
      // if you're setting it in a .env file
      RUBY_ENV: "development",
      BUNDLE_APP_CONFIG: "/azk/bundler",
    },
  },
  postgres: {
    // Dependent systems
    depends: [],
    // More images:  http://images.azk.io
    image: {"docker": "azukiapp/postgres:9.4"},
    shell: "/bin/bash",
    wait: 150,
    mounts: {
      '/var/lib/postgresql/data': persistent("postgres-data"),
      '/var/log/postgresql': persistent("postgres-log"),
    },
    ports: {
      // exports global variables
      data: "5432/tcp",
    },
    envs: {
      // set instances variables
      POSTGRES_USER: "azk",
      POSTGRES_PASS: "azk",
      POSTGRES_DB: "azk",
    },
    export_envs: {
      // exports variables for dependent systems
      DATABASE_URL: "postgres://#{envs.POSTGRES_USER}:#{envs.POSTGRES_PASS}@#{net.host}:#{net.port.data}/#{envs.POSTGRES_DATABASE}",
    },
  },
});
<|endoftext|>


<commit_before>var options = options || {};  // allows specifications to be added if the variable is already present

function read(URL, callback) {
    /**
    reads the contents of the file at the URL,
    converts it into a string,
    puts the string into a
, and then
    calls the callback function (which has no arguments)
    with "this" equalling the

    */
    var file = new XMLHttpRequest();
    file.open("GET", URL);  // Don't add false as an extra argument (browsers don't like it). (default: asynchronous=true)
    file.onreadystatechange = function () {
        if(file.readyState === 4) {  // Is it done?
            if(file.status === 200 || file.status == 0) {  // Was it successful?
                // file.responseXML might have something
                var container = document.createElement("div");
                container.innerHTML = file.responseText;
                /*
                var tags = container.children;
                for (var index=0; index<tags.length; index++) {
                    var tag = document.createElement(tags[index].tagName);
                    if (tag.tagName != "script") {
                        tag.appendChild(document.createTextNode(tags[index].innerHTML));
                        container.insertBefore(tag, tags[index]);
                        tags[index+1].outerHTML = "";
                    }
                }
                */
                // This is necessary because HTML5 doesn't think script tags and innerHTML should go together (for security reasons).
                var scripts = file.responseText.split(""))));
                            container.insertBefore(scriptTag, container.getElementsByTagName("script")[index-1]);
                            var oldTag = container.getElementsByTagName("script")[index];
                            oldTag.parentNode.removeChild(oldTag);
                        }
                    });
                }
                callback.call(container);  // .call(calling object / value of "this", function arguments (listed individually))  .apply has function arguments in an array
                // You could also use callback(argument(s)) like a normal function, but it wouldn't change the value of "this".
            }
        }
    }
    file.send();
}

function pageJump(ID) {
    /**
    makes a section to jump to certain parts of the page
    */
    document.getElementsByTagName("h1")[0].id = "top";
    var division = document.getElementById(ID);
    var contents = document.createElement("div");
    contents.className = "list";
    contents.innerHTML = "

Jump to:
";
    var sections = division.getElementsByTagName("h2");
    var toTop = document.createElement("a");
    toTop.href = "#top";
    toTop.innerHTML = "To top";
    var listItems = "";  // I need this variable to hold the list items because trying to add
 at the beginning and
 at the end separately causes it to fill in the
 immediately after the
 and omit the real

    for (var index=0; index<sections.length; index++) {  // I would use (var in array), but index exceeds entries.length for no apparent reason
        var inside = sections[index].innerHTML.trim();  // The inner HTML has a bunch of whitespace for no apparent reason.
        sections[index].id = inside;
        listItems += "
" + inside + "
";
        division.insertBefore(toTop.cloneNode(true), division.getElementsByTagName("h2")[index].nextSibling);  // inserts after

        // toTop needs to be cloned so it doesn't keep getting reasigned to the next place (it also needs to have true to clone all children of the node, although it doesn't apply here)
    }
    contents.innerHTML += "
" + listItems + "

";
    division.parentNode.insertBefore(contents, division);  // .insertBefore() only works for the immediate descendants of the parent
}

//This is able to run without waiting for anything else to load.

// makes my custom tag which underlines things
document.createElement("under");
// makes my custom tag which overlines things
document.createElement("over");

// adds the universal formatting
var style = document.createElement("link");
style.rel = "stylesheet";
style.href = "https://coolprogramminguser.github.io/Standards/formatting.css";
document.head.insertBefore(style, document.head.children[0]);

// links a favicon
var faviconNumber = 1;
var icon = document.createElement("link");
icon.rel = "icon";
document.head.insertBefore(icon, document.head.children[0]);

// cycles the favicon
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var color = 0;
context.beginPath();
context.arc(canvas.width/2, canvas.height/2, 64, 0, 2*Math.PI);
setInterval(function() {
    if (color >= 360) {
        color = 0;
    }
    context.fillStyle = "hsl(" + color + ", 100%, 50%)";
    context.fill();
    document.getElementById("icon").href = canvas.toDataURL();
    color++;
}, 20);

window.addEventListener("load", function() {  // This waits for everything past the script import to load before running.

    // surrounds the  content with a
 tag
    document.body.innerHTML = "
" + document.body.innerHTML + "
";

    // adds a title to the page
    var title = document.createElement("h1");
    title.style.color = "white";
    title.innerHTML = document.title;
    document.body.insertBefore(title, document.body.children[0]);

    // surrounds every list with

    var orderedLists = document.getElementsByTagName("ol");
    var unorderedLists = document.getElementsByTagName("ul");
    for (var index=0; index<orderedLists.length; index++) {
        orderedLists[index].outerHTML = "
" + orderedLists[index].outerHTML + "
";
        orderedLists[index].style.visibility = "visible";
    }
    for (var index=0; index<unorderedLists.length; index++) {
        unorderedLists[index].outerHTML = "
" + unorderedLists[index].outerHTML + "
";
        unorderedLists[index].style.visibility = "visible";
    }

    // interprets condensed tables
    var tables = document.getElementsByClassName("compact");
    for (var counter=0; counter<tables.length; counter++) {
        var table = tables[counter];
        var headings = table.getElementsByTagName("th");
        for (var index=0; index<headings.length; index++) {
            var parent = headings[index].parentNode;
            var newHeadings = headings[index].innerHTML.split("|");
            parent.removeChild(headings[index]);
            newHeadings.forEach(function(heading) {
                parent.innerHTML += "" + heading.trim() + "	";
            });
        }
        var data = table.getElementsByTagName("td");
        for (var index=0; index<data.length; index++) {
            var parent = data[index].parentNode;
            var newData = data[index].innerHTML.split("|");
            parent.removeChild(data[index]);
            newData.forEach(function(item) {
                parent.innerHTML += "" + item.trim() + "	";
            });
        }
        table.style.visibility = "visible";
    }

    // handles the options
    for (var spec in options) {
        switch (spec) {
            case "navigation":
                document.body.style = "margin:0vw 0vh 0vh 15vw; width: 85%;";
                read(options[spec], function() {
                    this.className = "nav";
                    document.body.insertBefore(this, document.body.childNodes[0]);
                });
                break;
        }
    }
});
<commit_msg>got rid of the copied document.getElementById()<commit_after>var options = options || {};  // allows specifications to be added if the variable is already present

function read(URL, callback) {
    /**
    reads the contents of the file at the URL,
    converts it into a string,
    puts the string into a
, and then
    calls the callback function (which has no arguments)
    with "this" equalling the

    */
    var file = new XMLHttpRequest();
    file.open("GET", URL);  // Don't add false as an extra argument (browsers don't like it). (default: asynchronous=true)
    file.onreadystatechange = function () {
        if(file.readyState === 4) {  // Is it done?
            if(file.status === 200 || file.status == 0) {  // Was it successful?
                // file.responseXML might have something
                var container = document.createElement("div");
                container.innerHTML = file.responseText;
                /*
                var tags = container.children;
                for (var index=0; index<tags.length; index++) {
                    var tag = document.createElement(tags[index].tagName);
                    if (tag.tagName != "script") {
                        tag.appendChild(document.createTextNode(tags[index].innerHTML));
                        container.insertBefore(tag, tags[index]);
                        tags[index+1].outerHTML = "";
                    }
                }
                */
                // This is necessary because HTML5 doesn't think script tags and innerHTML should go together (for security reasons).
                var scripts = file.responseText.split(""))));
                            container.insertBefore(scriptTag, container.getElementsByTagName("script")[index-1]);
                            var oldTag = container.getElementsByTagName("script")[index];
                            oldTag.parentNode.removeChild(oldTag);
                        }
                    });
                }
                callback.call(container);  // .call(calling object / value of "this", function arguments (listed individually))  .apply has function arguments in an array
                // You could also use callback(argument(s)) like a normal function, but it wouldn't change the value of "this".
            }
        }
    }
    file.send();
}

function pageJump(ID) {
    /**
    makes a section to jump to certain parts of the page
    */
    document.getElementsByTagName("h1")[0].id = "top";
    var division = document.getElementById(ID);
    var contents = document.createElement("div");
    contents.className = "list";
    contents.innerHTML = "

Jump to:
";
    var sections = division.getElementsByTagName("h2");
    var toTop = document.createElement("a");
    toTop.href = "#top";
    toTop.innerHTML = "To top";
    var listItems = "";  // I need this variable to hold the list items because trying to add
 at the beginning and
 at the end separately causes it to fill in the
 immediately after the
 and omit the real

    for (var index=0; index<sections.length; index++) {  // I would use (var in array), but index exceeds entries.length for no apparent reason
        var inside = sections[index].innerHTML.trim();  // The inner HTML has a bunch of whitespace for no apparent reason.
        sections[index].id = inside;
        listItems += "
" + inside + "
";
        division.insertBefore(toTop.cloneNode(true), division.getElementsByTagName("h2")[index].nextSibling);  // inserts after

        // toTop needs to be cloned so it doesn't keep getting reasigned to the next place (it also needs to have true to clone all children of the node, although it doesn't apply here)
    }
    contents.innerHTML += "
" + listItems + "

";
    division.parentNode.insertBefore(contents, division);  // .insertBefore() only works for the immediate descendants of the parent
}

//This is able to run without waiting for anything else to load.

// makes my custom tag which underlines things
document.createElement("under");
// makes my custom tag which overlines things
document.createElement("over");

// adds the universal formatting
var style = document.createElement("link");
style.rel = "stylesheet";
style.href = "https://coolprogramminguser.github.io/Standards/formatting.css";
document.head.insertBefore(style, document.head.children[0]);

// links a favicon
var faviconNumber = 1;
var icon = document.createElement("link");
icon.rel = "icon";
document.head.insertBefore(icon, document.head.children[0]);

// cycles the favicon
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var color = 0;
context.beginPath();
context.arc(canvas.width/2, canvas.height/2, 64, 0, 2*Math.PI);
setInterval(function() {
    if (color >= 360) {
        color = 0;
    }
    context.fillStyle = "hsl(" + color + ", 100%, 50%)";
    context.fill();
    icon.href = canvas.toDataURL();
    color++;
}, 20);

window.addEventListener("load", function() {  // This waits for everything past the script import to load before running.

    // surrounds the  content with a
 tag
    document.body.innerHTML = "
" + document.body.innerHTML + "
";

    // adds a title to the page
    var title = document.createElement("h1");
    title.style.color = "white";
    title.innerHTML = document.title;
    document.body.insertBefore(title, document.body.children[0]);

    // surrounds every list with

    var orderedLists = document.getElementsByTagName("ol");
    var unorderedLists = document.getElementsByTagName("ul");
    for (var index=0; index<orderedLists.length; index++) {
        orderedLists[index].outerHTML = "
" + orderedLists[index].outerHTML + "
";
        orderedLists[index].style.visibility = "visible";
    }
    for (var index=0; index<unorderedLists.length; index++) {
        unorderedLists[index].outerHTML = "
" + unorderedLists[index].outerHTML + "
";
        unorderedLists[index].style.visibility = "visible";
    }

    // interprets condensed tables
    var tables = document.getElementsByClassName("compact");
    for (var counter=0; counter<tables.length; counter++) {
        var table = tables[counter];
        var headings = table.getElementsByTagName("th");
        for (var index=0; index<headings.length; index++) {
            var parent = headings[index].parentNode;
            var newHeadings = headings[index].innerHTML.split("|");
            parent.removeChild(headings[index]);
            newHeadings.forEach(function(heading) {
                parent.innerHTML += "" + heading.trim() + "	";
            });
        }
        var data = table.getElementsByTagName("td");
        for (var index=0; index<data.length; index++) {
            var parent = data[index].parentNode;
            var newData = data[index].innerHTML.split("|");
            parent.removeChild(data[index]);
            newData.forEach(function(item) {
                parent.innerHTML += "" + item.trim() + "	";
            });
        }
        table.style.visibility = "visible";
    }

    // handles the options
    for (var spec in options) {
        switch (spec) {
            case "navigation":
                document.body.style = "margin:0vw 0vh 0vh 15vw; width: 85%;";
                read(options[spec], function() {
                    this.className = "nav";
                    document.body.insertBefore(this, document.body.childNodes[0]);
                });
                break;
        }
    }
});
<|endoftext|>


<commit_before>/**
 * Copyright 2021 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview
 * Creates a Github issue for an upcoming design review.
 *
 * https://go.amp.dev/design-reviews
 *
 * A Github Action runs this once a week. See create-design-review-issues.yml
 */

/*
 * ⚠ Only use standard node modules.
 *
 * This file runs by itself. It cannot depend on `npm install` nor file
 * structure since the Github Action downloads this file only.
 */
const https = require('https');

const dayOfWeek = /* wednesday */ 3; // sunday = 0, monday = 1, ...

const sessionDurationHours = 1;

const timeRotationUtc = [
  ['Americas', '21:00'],
  ['Asia/Oceania', '01:00'],
  ['Africa/Europe/western Asia', '16:30'],
];

const timeRotationStartYyyyMmDd = '2021-03-17';

// All previous weeks have already been handled.
const generateWeeksFromNow = 3;

const labels = ['Type: Design Review'];

const createTitle = ({yyyyMmDd, timeUtc, region}) =>
  `Design Review ${yyyyMmDd} ${timeUtc} UTC (${region})`;

const vcUrl = 'https://bit.ly/amp-dr';
const calendarEventTitle = 'AMP Project Design Review';
const calendarEventDetails = vcUrl;

const createBody = ({timeUtc, timeUrl, calendarUrl}) =>
  `
Time: [${timeUtc} UTC](${timeUrl}) ([add to Google Calendar](${calendarUrl}))
Location: [Video conference via Google Meet](${vcUrl})

The AMP community holds weekly engineering [design reviews](https://github.com/ampproject/amphtml/blob/master/contributing/design-reviews.md). **We encourage everyone in the community to participate in these design reviews.**

If you are interested in bringing your design to design review, read the [design review documentation](https://github.com/ampproject/amphtml/blob/master/contributing/design-reviews.md) and add a link to your design doc or issue by the Monday before your design review.

When attending a design review please read through the designs _before_ the design review starts. This allows us to spend more time on discussion of the design.

We rotate our design review between times that work better for different parts of the world as described in our [design review documentation](https://github.com/ampproject/amphtml/blob/master/contributing/design-reviews.md), but you are welcome to attend any design review. If you cannot make any of the design reviews but have a design to discuss please let mrjoro@ know on [Slack](https://github.com/ampproject/amphtml/blob/master/CONTRIBUTING.md#discussion-channels) and we will find a time that works for you.
`.trim();

function httpsRequest(url, options, data) {
  return new Promise((resolve, reject) => {
    const req = https.request(url, options, (res) => {
      const chunks = [];
      res.on('data', (chunk) => {
        chunks.push(Buffer.from(chunk));
      });
      res.on('close', () => {
        const body = Buffer.concat(chunks).toString('utf-8');
        resolve({res, body});
      });
    });
    req.on('error', (error) => {
      reject(error);
    });
    req.write(data);
    req.end();
  });
}

async function postGithub(token, url, data) {
  const {res, body} = await httpsRequest(
    url,
    {
      method: 'POST',
      headers: {
        'Authorization': `token ${token}`,
        'Content-Type': 'application/json',
        'User-Agent': 'amphtml',
        'Accept': 'application/vnd.github.v3+json',
      },
    },
    JSON.stringify(data)
  );

  if (res.statusCode < 200 || res.statusCode > 299) {
    console./*OK*/ error(body);
    throw new Error(res.statusCode);
  }

  return JSON.parse(body);
}

function postGithubIssue(token, repo, data) {
  const url = `https://api.github.com/repos/${repo}/issues`;
  return postGithub(token, url, data);
}

function getNextDayOfWeek(date, dayOfWeek, weeks = 1) {
  const resultDate = new Date(date.getTime());
  resultDate.setDate(
    resultDate.getDate() +
      (weeks - 1) * 7 +
      ((7 + dayOfWeek - date.getDay()) % 7)
  );
  return resultDate;
}

function getRotation(date, startYyyyMmDd) {
  const [year, month, day] = startYyyyMmDd.split('-');
  const start = new Date(year, month - 1, day);
  const dateBeginningOfDay = new Date(
    date.getFullYear(),
    date.getMonth(),
    date.getDate()
  );
  const weeks = Math.round(
    (dateBeginningOfDay - start) / (7 * 24 * 60 * 60 * 1000)
  );
  return timeRotationUtc[weeks % timeRotationUtc.length];
}

const timeZ = (yyyy, mm, dd, hours, minutes) =>
  `${yyyy + mm + dd}T${
    hours.toString().padStart(2, '0') + minutes.toString().padStart(2, '0')
  }Z`;

function getNextIssueData() {
  const today = new Date();

  // if we run on the same day of week, we need to skip one day to calculate
  // properly
  today.setDate(today.getDate() + 1);

  const nextDay = getNextDayOfWeek(today, dayOfWeek, generateWeeksFromNow);
  const [region, timeUtc] = getRotation(nextDay, timeRotationStartYyyyMmDd);
  const [hours, minutes] = timeUtc.split(':').map(Number);

  const yyyy = nextDay.getFullYear();
  const mm = (nextDay.getMonth() + 1).toString().padStart(2, '0');
  const dd = nextDay.getDate().toString().padStart(2, '0');

  const timeUrl = `https://www.timeanddate.com/worldclock/meeting.html?year=${yyyy}&month=${mm}&day=${dd}&iv=0`;

  const startZ = timeZ(yyyy, mm, dd, hours, minutes);
  const endZ = timeZ(yyyy, mm, dd, hours + sessionDurationHours, minutes);

  const calendarUrl = `http://www.google.com/calendar/event?action=TEMPLATE&text=${encodeURIComponent(
    calendarEventTitle
  )}&dates=${startZ}/${endZ}&details=${encodeURIComponent(
    calendarEventDetails
  )}`;

  const templateData = {
    yyyyMmDd: `${yyyy}-${mm}-${dd}`,
    timeUtc,
    region,
    timeUrl,
    calendarUrl,
  };

  const title = createTitle(templateData);
  const body = createBody(templateData);

  return {title, labels, body};
}

function env(key) {
  if (!(key in process.env)) {
    throw new Error(`Missing env variable: ${key}`);
  }
  return process.env[key];
}

async function createDesignReviewIssue() {
  const repo = env('GITHUB_REPOSITORY');
  if (repo !== 'ampproject/amphtml') {
    // don't run on forks.
    return;
  }
  const {title, 'html_url': htmlUrl} = await postGithubIssue(
    env('GITHUB_TOKEN'),
    repo,
    getNextIssueData()
  );
  console./*OK*/ log(title);
  console./*OK*/ log(htmlUrl);
}

createDesignReviewIssue().catch((e) => {
  console./*OK*/ error(e);
  process.exit(1);
});
<commit_msg>🏗 Shift design review rotation to match scheduled meetings (#33457)<commit_after>/**
 * Copyright 2021 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview
 * Creates a Github issue for an upcoming design review.
 *
 * https://go.amp.dev/design-reviews
 *
 * A Github Action runs this once a week. See create-design-review-issues.yml
 */

/*
 * ⚠ Only use standard node modules.
 *
 * This file runs by itself. It cannot depend on `npm install` nor file
 * structure since the Github Action downloads this file only.
 */
const https = require('https');

const dayOfWeek = /* wednesday */ 3; // sunday = 0, monday = 1, ...

const sessionDurationHours = 1;

const timeRotationUtc = [
  ['Americas', '21:00'],
  ['Asia/Oceania', '01:00'],
  ['Africa/Europe/western Asia', '16:30'],
];

const timeRotationStartYyyyMmDd = '2021-03-31';

// All previous weeks have already been handled.
const generateWeeksFromNow = 3;

const labels = ['Type: Design Review'];

const createTitle = ({yyyyMmDd, timeUtc, region}) =>
  `Design Review ${yyyyMmDd} ${timeUtc} UTC (${region})`;

const vcUrl = 'https://bit.ly/amp-dr';
const calendarEventTitle = 'AMP Project Design Review';
const calendarEventDetails = vcUrl;

const createBody = ({timeUtc, timeUrl, calendarUrl}) =>
  `
Time: [${timeUtc} UTC](${timeUrl}) ([add to Google Calendar](${calendarUrl}))
Location: [Video conference via Google Meet](${vcUrl})

The AMP community holds weekly engineering [design reviews](https://github.com/ampproject/amphtml/blob/master/contributing/design-reviews.md). **We encourage everyone in the community to participate in these design reviews.**

If you are interested in bringing your design to design review, read the [design review documentation](https://github.com/ampproject/amphtml/blob/master/contributing/design-reviews.md) and add a link to your design doc or issue by the Monday before your design review.

When attending a design review please read through the designs _before_ the design review starts. This allows us to spend more time on discussion of the design.

We rotate our design review between times that work better for different parts of the world as described in our [design review documentation](https://github.com/ampproject/amphtml/blob/master/contributing/design-reviews.md), but you are welcome to attend any design review. If you cannot make any of the design reviews but have a design to discuss please let mrjoro@ know on [Slack](https://github.com/ampproject/amphtml/blob/master/CONTRIBUTING.md#discussion-channels) and we will find a time that works for you.
`.trim();

function httpsRequest(url, options, data) {
  return new Promise((resolve, reject) => {
    const req = https.request(url, options, (res) => {
      const chunks = [];
      res.on('data', (chunk) => {
        chunks.push(Buffer.from(chunk));
      });
      res.on('close', () => {
        const body = Buffer.concat(chunks).toString('utf-8');
        resolve({res, body});
      });
    });
    req.on('error', (error) => {
      reject(error);
    });
    req.write(data);
    req.end();
  });
}

async function postGithub(token, url, data) {
  const {res, body} = await httpsRequest(
    url,
    {
      method: 'POST',
      headers: {
        'Authorization': `token ${token}`,
        'Content-Type': 'application/json',
        'User-Agent': 'amphtml',
        'Accept': 'application/vnd.github.v3+json',
      },
    },
    JSON.stringify(data)
  );

  if (res.statusCode < 200 || res.statusCode > 299) {
    console./*OK*/ error(body);
    throw new Error(res.statusCode);
  }

  return JSON.parse(body);
}

function postGithubIssue(token, repo, data) {
  const url = `https://api.github.com/repos/${repo}/issues`;
  return postGithub(token, url, data);
}

function getNextDayOfWeek(date, dayOfWeek, weeks = 1) {
  const resultDate = new Date(date.getTime());
  resultDate.setDate(
    resultDate.getDate() +
      (weeks - 1) * 7 +
      ((7 + dayOfWeek - date.getDay()) % 7)
  );
  return resultDate;
}

function getRotation(date, startYyyyMmDd) {
  const [year, month, day] = startYyyyMmDd.split('-');
  const start = new Date(year, month - 1, day);
  const dateBeginningOfDay = new Date(
    date.getFullYear(),
    date.getMonth(),
    date.getDate()
  );
  const weeks = Math.round(
    (dateBeginningOfDay - start) / (7 * 24 * 60 * 60 * 1000)
  );
  return timeRotationUtc[weeks % timeRotationUtc.length];
}

const timeZ = (yyyy, mm, dd, hours, minutes) =>
  `${yyyy + mm + dd}T${
    hours.toString().padStart(2, '0') + minutes.toString().padStart(2, '0')
  }Z`;

function getNextIssueData() {
  const today = new Date();

  // if we run on the same day of week, we need to skip one day to calculate
  // properly
  today.setDate(today.getDate() + 1);

  const nextDay = getNextDayOfWeek(today, dayOfWeek, generateWeeksFromNow);
  const [region, timeUtc] = getRotation(nextDay, timeRotationStartYyyyMmDd);
  const [hours, minutes] = timeUtc.split(':').map(Number);

  const yyyy = nextDay.getFullYear();
  const mm = (nextDay.getMonth() + 1).toString().padStart(2, '0');
  const dd = nextDay.getDate().toString().padStart(2, '0');

  const timeUrl = `https://www.timeanddate.com/worldclock/meeting.html?year=${yyyy}&month=${mm}&day=${dd}&iv=0`;

  const startZ = timeZ(yyyy, mm, dd, hours, minutes);
  const endZ = timeZ(yyyy, mm, dd, hours + sessionDurationHours, minutes);

  const calendarUrl = `http://www.google.com/calendar/event?action=TEMPLATE&text=${encodeURIComponent(
    calendarEventTitle
  )}&dates=${startZ}/${endZ}&details=${encodeURIComponent(
    calendarEventDetails
  )}`;

  const templateData = {
    yyyyMmDd: `${yyyy}-${mm}-${dd}`,
    timeUtc,
    region,
    timeUrl,
    calendarUrl,
  };

  const title = createTitle(templateData);
  const body = createBody(templateData);

  return {title, labels, body};
}

function env(key) {
  if (!(key in process.env)) {
    throw new Error(`Missing env variable: ${key}`);
  }
  return process.env[key];
}

async function createDesignReviewIssue() {
  const repo = env('GITHUB_REPOSITORY');
  if (repo !== 'ampproject/amphtml') {
    // don't run on forks.
    return;
  }
  const {title, 'html_url': htmlUrl} = await postGithubIssue(
    env('GITHUB_TOKEN'),
    repo,
    getNextIssueData()
  );
  console./*OK*/ log(title);
  console./*OK*/ log(htmlUrl);
}

createDesignReviewIssue().catch((e) => {
  console./*OK*/ error(e);
  process.exit(1);
});
<|endoftext|>


<commit_before>import { drawDev } from '/imports/ui/helpers/dev';
import { drawPlaces, hidePlaces } from '/imports/ui/helpers/drawPlaces';
import { mapProjection } from '/imports/ui/helpers/mapProjection';
import { appDurations, appSizes } from '/imports/ui/helpers/settings';
import {
  collapseSidebar, drawSidebar, expandSidebar, highlightImage
} from '/imports/ui/helpers/sidebar';

const sizes = appSizes();
const dur = appDurations();

/**
 * Create the D3 chart object
 */
let d3Chart = {};
d3Chart.create = function (el, props, state) {

  /**
   * Set up the homepage map
   */
  this._drawMap(el, props, state);

  /**
   * Set up the homepage sidebar
   */
  drawSidebar();

  /**
   * Handle clicks on homepage places
   *
   * It's important to use the .on() syntax which can handle
   * looking for elements that are animating into the page
   * over time (animation).
   */
  $(document).on('click', '.place-label', function () {
    drawDetailPage(this, state);
  });

  /**
   * Handle click on the detail page home button
   */
  $(document).on('click', '.home-button', function () {
    reDrawHomePage(el, state);
  });

  /**
   * Handle click on the dock images
   */
  $(document).on('click', '.image-thumbnail:not(.active)', function () {
    highlightImage(this, el, state);
  });

};

/**
 * Draw the homepage map when the app is loaded
 * @param el
 * @param props
 * @param state
 * @private
 */
d3Chart._drawMap = function (el, props, state) {
  const sizes = appSizes();
  let svg = d3.select(el).append('svg')
    .attr('class', 'd3')
    .attr('width', props.width)
    .attr('height', props.height);

  // Add SVG background image
  svg.append('svg:image')
    .attr('id', 'background-image')
    .attr('x', 0)
    .attr('y', 0)
    .attr('width', sizes.screenWidth)
    .attr('height', sizes.screenHeight)
    .attr('xlink:href', '/images/mnrra_be09d4.png');

  // Add a group for the points
  svg.append('g')
    .attr('class', 'map-points');

  // Draw development tools if enabled
  drawDev(el, props, mapProjection(state.settings));

  // Draw all of the places on the map
  drawPlaces(
    el,
    mapProjection(state.settings),
    state.places,
    state.images,
  );

};

/**
 * Draw the details page, when a user selects a location
 * @param clicked The clicked DOM element
 * @param {object} state Meteor data via React state
 */
function drawDetailPage(clicked, state) {

  // Selected place ID
  let selectedPlaceId = $(clicked).attr('id');

  // ID of the image in the selected place
  // This will be highlighted in the sidebar
  let selectedPlaceImageId = $(clicked)
    .children('.place-label-background')
    .attr('id');

  // Get Meteor data object for the selected place
  let selectedPlace = _.find(state.places, function (o) {
    return o.slug == selectedPlaceId;
  });

  // Animate out the places that were not clicked
  hidePlaces(
    selectedPlaceId,
    mapProjection(state.settings),
    state.places,
  );

  // Center the map on the selected location
  zoomMap(state, selectedPlace);

  // Expand the sidebar and add clicked content
  expandSidebar(state, selectedPlace, selectedPlaceImageId);

  // Draw the home button
  drawHomeButton();
}

/**
 * Zoom and pan the background map to a selected location
 * @param state
 * @param selectedPlace
 */
function zoomMap(state, selectedPlace) {

  let projection = mapProjection(state.settings);
  let line = {};
  line.x1 = projection([selectedPlace.long, selectedPlace.lat])[0];
  line.y1 = projection([selectedPlace.long, selectedPlace.lat])[1];

  // Zoom and position background image
  d3.select('#background-image')
    .transition()
    .duration(dur.bgZoom)
    .attr('width', sizes.screenWidth * 2)
    .attr('height', sizes.screenHeight * 2)
    .attr(
      'x',
      (
        (line.x1 * -2) +
        ((sizes.screenWidth - sizes.infoWidthExpanded) / 2)
      )
    )
    .attr('y', (line.y1 * -1));

  // Use in the future if you draw a bigger map
  // that can accommodate the overflow in the Y axis
  // .attr('y', ((line.y1 * -2) + (screenHeight / 2)));

}

/**
 * Draw home button
 */
function drawHomeButton() {
  // Add home button
  let $homeButton = $('<div/>')
    .addClass('home-button')
    .html('Home');
  $('.Chart')
    .append($homeButton);

  // Position home button in center of map panel
  $homeButton.css(
    'left',
    ((sizes.screenWidth - sizes.infoWidthExpanded) / 2) -
    ($homeButton.outerWidth() / 2)
  );
}

/**
 * Draw homepage on return from the detail view
 * @param el
 * @param state
 */
function reDrawHomePage(el, state) {
  collapseSidebar();

  // Reset the background map
  d3.select('#background-image')
    .transition()
    .duration(300)
    .attr('width', sizes.screenWidth)
    .attr('height', sizes.screenHeight)
    .attr('x', 0)
    .attr('y', 0);

  // Remove home button
  $('div.home-button').remove();

  drawPlaces(
    el,
    mapProjection(state.settings),
    state.places,
    state.images,
  );
}

/**
 * Update the D3 chart when the React props change
 * @param el
 * @param props
 * @param state
 */
d3Chart.update = function (el, props, state) {
  const mapPath = d3.select('path.states');
  mapPath.remove();
  this._drawMap(el, props, state);
};

export default d3Chart;
<commit_msg>Unused parameter<commit_after>import { drawDev } from '/imports/ui/helpers/dev';
import { drawPlaces, hidePlaces } from '/imports/ui/helpers/drawPlaces';
import { mapProjection } from '/imports/ui/helpers/mapProjection';
import { appDurations, appSizes } from '/imports/ui/helpers/settings';
import {
  collapseSidebar, drawSidebar, expandSidebar, highlightImage
} from '/imports/ui/helpers/sidebar';

const sizes = appSizes();
const dur = appDurations();

/**
 * Create the D3 chart object
 */
let d3Chart = {};
d3Chart.create = function (el, props, state) {

  /**
   * Set up the homepage map
   */
  this._drawMap(el, props, state);

  /**
   * Set up the homepage sidebar
   */
  drawSidebar();

  /**
   * Handle clicks on homepage places
   *
   * It's important to use the .on() syntax which can handle
   * looking for elements that are animating into the page
   * over time (animation).
   */
  $(document).on('click', '.place-label', function () {
    drawDetailPage(this, state);
  });

  /**
   * Handle click on the detail page home button
   */
  $(document).on('click', '.home-button', function () {
    reDrawHomePage(el, state);
  });

  /**
   * Handle click on the dock images
   */
  $(document).on('click', '.image-thumbnail:not(.active)', function () {
    highlightImage(this, state);
  });

};

/**
 * Draw the homepage map when the app is loaded
 * @param el
 * @param props
 * @param state
 * @private
 */
d3Chart._drawMap = function (el, props, state) {
  const sizes = appSizes();
  let svg = d3.select(el).append('svg')
    .attr('class', 'd3')
    .attr('width', props.width)
    .attr('height', props.height);

  // Add SVG background image
  svg.append('svg:image')
    .attr('id', 'background-image')
    .attr('x', 0)
    .attr('y', 0)
    .attr('width', sizes.screenWidth)
    .attr('height', sizes.screenHeight)
    .attr('xlink:href', '/images/mnrra_be09d4.png');

  // Add a group for the points
  svg.append('g')
    .attr('class', 'map-points');

  // Draw development tools if enabled
  drawDev(el, props, mapProjection(state.settings));

  // Draw all of the places on the map
  drawPlaces(
    el,
    mapProjection(state.settings),
    state.places,
    state.images,
  );

};

/**
 * Draw the details page, when a user selects a location
 * @param clicked The clicked DOM element
 * @param {object} state Meteor data via React state
 */
function drawDetailPage(clicked, state) {

  // Selected place ID
  let selectedPlaceId = $(clicked).attr('id');

  // ID of the image in the selected place
  // This will be highlighted in the sidebar
  let selectedPlaceImageId = $(clicked)
    .children('.place-label-background')
    .attr('id');

  // Get Meteor data object for the selected place
  let selectedPlace = _.find(state.places, function (o) {
    return o.slug == selectedPlaceId;
  });

  // Animate out the places that were not clicked
  hidePlaces(
    selectedPlaceId,
    mapProjection(state.settings),
    state.places,
  );

  // Center the map on the selected location
  zoomMap(state, selectedPlace);

  // Expand the sidebar and add clicked content
  expandSidebar(state, selectedPlace, selectedPlaceImageId);

  // Draw the home button
  drawHomeButton();
}

/**
 * Zoom and pan the background map to a selected location
 * @param state
 * @param selectedPlace
 */
function zoomMap(state, selectedPlace) {

  let projection = mapProjection(state.settings);
  let line = {};
  line.x1 = projection([selectedPlace.long, selectedPlace.lat])[0];
  line.y1 = projection([selectedPlace.long, selectedPlace.lat])[1];

  // Zoom and position background image
  d3.select('#background-image')
    .transition()
    .duration(dur.bgZoom)
    .attr('width', sizes.screenWidth * 2)
    .attr('height', sizes.screenHeight * 2)
    .attr(
      'x',
      (
        (line.x1 * -2) +
        ((sizes.screenWidth - sizes.infoWidthExpanded) / 2)
      )
    )
    .attr('y', (line.y1 * -1));

  // Use in the future if you draw a bigger map
  // that can accommodate the overflow in the Y axis
  // .attr('y', ((line.y1 * -2) + (screenHeight / 2)));

}

/**
 * Draw home button
 */
function drawHomeButton() {
  // Add home button
  let $homeButton = $('<div/>')
    .addClass('home-button')
    .html('Home');
  $('.Chart')
    .append($homeButton);

  // Position home button in center of map panel
  $homeButton.css(
    'left',
    ((sizes.screenWidth - sizes.infoWidthExpanded) / 2) -
    ($homeButton.outerWidth() / 2)
  );
}

/**
 * Draw homepage on return from the detail view
 * @param el
 * @param state
 */
function reDrawHomePage(el, state) {
  collapseSidebar();

  // Reset the background map
  d3.select('#background-image')
    .transition()
    .duration(300)
    .attr('width', sizes.screenWidth)
    .attr('height', sizes.screenHeight)
    .attr('x', 0)
    .attr('y', 0);

  // Remove home button
  $('div.home-button').remove();

  drawPlaces(
    el,
    mapProjection(state.settings),
    state.places,
    state.images,
  );
}

/**
 * Update the D3 chart when the React props change
 * @param el
 * @param props
 * @param state
 */
d3Chart.update = function (el, props, state) {
  const mapPath = d3.select('path.states');
  mapPath.remove();
  this._drawMap(el, props, state);
};

export default d3Chart;
<|endoftext|>


<commit_before>const { app, BrowserWindow, ipcMain, shell } = require('electron');
const fs = require('fs-extra');
const storage = require('electron-json-storage');
const _ = require('lodash');
const SWProxy = require('./proxy/SWProxy');

const path = require('path');
const url = require('url');

global.gMapping = require('./mapping');

let defaultFilePath = path.join(app.getPath('desktop'), `${app.getName()} Files`);
let defaultConfig = {
  Config: {
    App: { filesPath: defaultFilePath, debug: false, clearLogOnLogin: false },
    Proxy: { port: 8080, autoStart: false },
    Plugins: {}
  }
};
let defaultConfigDetails = {
  ConfigDetails: {
    App: { debug: { label: 'Show Debug Messages' }, clearLogOnLogin: { label: 'Clear Log on every login' } },
    Proxy: { autoStart: { label: 'Start proxy automatically' } },
    Plugins: {}
  }
};

function createWindow() {
  global.win = new BrowserWindow({
    minWidth: 800,
    minHeight: 600,
    acceptFirstMouse: true,
    autoHideMenuBar: true
  });

  global.mainWindowId = win.id;

  win.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
  }));

  win.webContents.on('new-window', (e, link) => {
    e.preventDefault();
    shell.openExternal(link);
  });
}

const proxy = new SWProxy();

proxy.on('error', () => {

});

ipcMain.on('proxyIsRunning', (event) => {
  event.returnValue = proxy.isRunning();
});

ipcMain.on('proxyGetInterfaces', (event) => {
  event.returnValue = proxy.getInterfaces();
});

ipcMain.on('proxyStart', () => {
  proxy.start(config.Config.Proxy.port);
});

ipcMain.on('proxyStop', () => {
  proxy.stop();
});

ipcMain.on('logGetEntries', (event) => {
  event.returnValue = proxy.getLogEntries();
});

ipcMain.on('updateConfig', () => {
  storage.set('Config', config.Config, (error) => {
    if (error) throw error;
  });
});

ipcMain.on('getFolderLocations', (event) => {
  event.returnValue = {
    settings: app.getPath('userData'),
    plugins: path.join(path.dirname(app.getPath('exe')), 'plugins')
  };
});

global.plugins = [];

function loadPlugins() {
  // Initialize Plugins
  let plugins = [];

  const pluginDir = path.join(__dirname, 'plugins');

  // Load each plugin module in the folder
  fs.readdirSync(pluginDir).forEach((file) => {
    plugins.push(require(path.join(pluginDir, file)));
  });

  // Initialize plugins
  plugins.forEach((plug) => {
    config.Config.Plugins[plug.pluginName] = _.merge(plug.defaultConfig, config.Config.Plugins[plug.pluginName]);
    config.ConfigDetails.Plugins[plug.pluginName] = plug.defaultConfigDetails || {};
    plug.init(proxy, config);
  });

  return plugins;
}

app.on('ready', () => {
  createWindow();

  storage.getAll((error, data) => {
    if (error) throw error;

    global.config = _.merge(defaultConfig, data);
    global.config.ConfigDetails = defaultConfigDetails.ConfigDetails;

    fs.ensureDirSync(global.config.Config.App.filesPath);

    global.plugins = loadPlugins();

    if (global.config.Config.Proxy.autoStart) {
      proxy.start(config.Config.Proxy.port);
    }
  });
});

app.on('window-all-closed', () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (win === null) {
    createWindow();
  }
});<commit_msg>Introduced two simple cmd variables<commit_after>const { app, BrowserWindow, ipcMain, shell } = require('electron');
const fs = require('fs-extra');
const storage = require('electron-json-storage');
const _ = require('lodash');
const SWProxy = require('./proxy/SWProxy');

const path = require('path');
const url = require('url');

global.gMapping = require('./mapping');

let defaultFilePath = path.join(app.getPath('desktop'), `${app.getName()} Files`);
let defaultConfig = {
  Config: {
    App: { filesPath: defaultFilePath, debug: false, clearLogOnLogin: false },
    Proxy: { port: 8080, autoStart: false },
    Plugins: {}
  }
};
let defaultConfigDetails = {
  ConfigDetails: {
    App: { debug: { label: 'Show Debug Messages' }, clearLogOnLogin: { label: 'Clear Log on every login' } },
    Proxy: { autoStart: { label: 'Start proxy automatically' } },
    Plugins: {}
  }
};

function createWindow() {
  global.win = new BrowserWindow({
    minWidth: 800,
    minHeight: 600,
    acceptFirstMouse: true,
    autoHideMenuBar: true
  });

  global.mainWindowId = win.id;

  win.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
  }));

  win.webContents.on('new-window', (e, link) => {
    e.preventDefault();
    shell.openExternal(link);
  });
}

const proxy = new SWProxy();

proxy.on('error', () => {

});

ipcMain.on('proxyIsRunning', (event) => {
  event.returnValue = proxy.isRunning();
});

ipcMain.on('proxyGetInterfaces', (event) => {
  event.returnValue = proxy.getInterfaces();
});

ipcMain.on('proxyStart', () => {
  proxy.start(config.Config.Proxy.port);
});

ipcMain.on('proxyStop', () => {
  proxy.stop();
});

ipcMain.on('logGetEntries', (event) => {
  event.returnValue = proxy.getLogEntries();
});

ipcMain.on('updateConfig', () => {
  storage.set('Config', config.Config, (error) => {
    if (error) throw error;
  });
});

ipcMain.on('getFolderLocations', (event) => {
  event.returnValue = {
    settings: app.getPath('userData'),
    plugins: path.join(path.dirname(app.getPath('exe')), 'plugins')
  };
});

global.plugins = [];

function loadPlugins() {
  // Initialize Plugins
  let plugins = [];

  const pluginDir = path.join(__dirname, 'plugins');

  // Load each plugin module in the folder
  fs.readdirSync(pluginDir).forEach((file) => {
    plugins.push(require(path.join(pluginDir, file)));
  });

  // Initialize plugins
  plugins.forEach((plug) => {
    config.Config.Plugins[plug.pluginName] = _.merge(plug.defaultConfig, config.Config.Plugins[plug.pluginName]);
    config.ConfigDetails.Plugins[plug.pluginName] = plug.defaultConfigDetails || {};
    plug.init(proxy, config);
  });

  return plugins;
}

app.on('ready', () => {
  createWindow();

  storage.getAll((error, data) => {
    if (error) throw error;

    global.config = _.merge(defaultConfig, data);
    global.config.ConfigDetails = defaultConfigDetails.ConfigDetails;

    fs.ensureDirSync(global.config.Config.App.filesPath);

    global.plugins = loadPlugins();

    if (process.env.autostart || global.config.Config.Proxy.autoStart) {
      proxy.start(process.env.port || config.Config.Proxy.port);
    }
  });
});

app.on('window-all-closed', () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (win === null) {
    createWindow();
  }
});<|endoftext|>