<commit_before>frappe.provide('frappe.desktop');

frappe.pages['desktop'].on_page_load = function(wrapper) {

	// load desktop
	if(!frappe.list_desktop) {
		frappe.desktop.set_background();
	}
	frappe.desktop.refresh(wrapper);
};

frappe.pages['desktop'].on_page_show = function(wrapper) {
	if(frappe.list_desktop) {
		$("body").attr("data-route", "list-desktop");
	}
};

$.extend(frappe.desktop, {
	refresh: function(wrapper) {
		if (wrapper) {
			this.wrapper = $(wrapper);
		}

		this.render();
		this.make_sortable();
	},

	render: function() {
		var me = this;
		frappe.utils.set_title(__("Desktop"));

		var template = frappe.list_desktop ? "desktop_list_view" : "desktop_icon_grid";

		var all_icons = frappe.get_desktop_icons();
		var explore_icon = {
			module_name: 'Explore',
			label: 'Explore',
			_label: __('Explore'),
			_id: 'Explore',
			_doctype: '',
			icon: 'octicon octicon-telescope',
			color: '#7578f6',
			link: 'modules'
		};
		explore_icon.app_icon = frappe.ui.app_icon.get_html(explore_icon);
		all_icons.push(explore_icon);

		frappe.desktop.wrapper.html(frappe.render_template(template, {
			// all visible icons
			desktop_items: all_icons,
		}));

		frappe.desktop.setup_module_click();

		// notifications
		frappe.desktop.show_pending_notifications();
		$(document).on("notification-update", function() {
			me.show_pending_notifications();
		});

		$(document).trigger("desktop-render");

	},

	render_help_messages: function(help_messages) {
		var wrapper = frappe.desktop.wrapper.find('.help-message-wrapper');
		var $help_messages = wrapper.find('.help-messages');

		var set_current_message = function(idx) {
			idx = cint(idx);
			wrapper.current_message_idx = idx;
			wrapper.find('.left-arrow, .right-arrow').addClass('disabled');
			wrapper.find('.help-message-item').addClass('hidden');
			wrapper.find('[data-message-idx="'+idx+'"]').removeClass('hidden');
			if(idx > 0) {
				wrapper.find('.left-arrow').removeClass('disabled');
			}
			if(idx < help_messages.length - 1) {
				wrapper.find('.right-arrow').removeClass('disabled');
			}
		}

		if(help_messages) {
			wrapper.removeClass('hidden');
			help_messages.forEach(function(message, i) {
				var $message = $('

')
					.attr('data-message-idx', i)
					.html(frappe.render_template('desktop_help_message', message))
					.appendTo($help_messages);

			});

			set_current_message(0);

			wrapper.find('.close').on('click', function() {
				wrapper.addClass('hidden');
			});
		}

		wrapper.find('.left-arrow').on('click', function() {
			if(wrapper.current_message_idx) {
				set_current_message(wrapper.current_message_idx - 1);
			}
		})

		wrapper.find('.right-arrow').on('click', function() {
			if(help_messages.length > wrapper.current_message_idx + 1) {
				set_current_message(wrapper.current_message_idx + 1);
			}
		});

	},

	setup_module_click: function() {
		var wiggling = false; // wiggle, wiggle, wiggle.

		if(frappe.list_desktop) {
			frappe.desktop.wrapper.on("click", ".desktop-list-item", function() {
				frappe.desktop.open_module($(this));
			});
		} else {
			frappe.desktop.wrapper.on("click", ".app-icon", function() {
				if ( !wiggling ) {
					frappe.desktop.open_module($(this).parent());
				}
			});
		}
		frappe.desktop.wrapper.on("click", ".circle", function() {
			var doctype = $(this).attr('data-doctype');
			if(doctype) {
				frappe.ui.notifications.show_open_count_list(doctype);
			}
		});

		// Wiggle, Wiggle, Wiggle.
		const DURATION_LONG_PRESS = 1000;
		// lesser the antidode, more the wiggle (like your drunk uncle)
		// 75 seems good to replicate the iOS feels.
		const WIGGLE_ANTIDODE     = 75;

		var   timer_id      = 0;
		const $cases        = frappe.desktop.wrapper.find('.case-wrapper');
		const $icons        = frappe.desktop.wrapper.find('.app-icon');
		const $notis        = $(frappe.desktop.wrapper.find('.circle').toArray().filter((object) => {
			// This hack is so bad, I should punch myself.
			const doctype   = $(object).data('doctype');

			return doctype;
		}));

		const clearWiggle   = () => {
			const $closes   = $cases.find('.module-remove');
			$closes.hide();
			$notis.show();

			$icons.trigger('stopRumble');
		};

		// initiate wiggling.
		$icons.jrumble({
			speed: WIGGLE_ANTIDODE // seems neat enough to match the iOS way
		});

		frappe.desktop.wrapper.on('mousedown', '.app-icon', () => {
			timer_id     = setTimeout(() => {
				wiggling = true;
				// hide all notifications.
				$notis.hide();

				$cases.each((i) => {
					const $case    = $($cases[i]);
					const template =
					`

									&times

					`;

					$case.append(template);
					const $close  = $case.find('.module-remove');
					const name    = $case.attr('title');
					$close.click(() => {
						// good enough to create dynamic dialogs?
						const dialog = new frappe.ui.Dialog({
							title: __(`Hide ${name}?`)
						});
						dialog.set_primary_action(__('Hide'), () => {
							frappe.call({
								method: 'frappe.desk.doctype.desktop_icon.desktop_icon.hide',
								args: { name: name },
								freeze: true,
								callback: (response) =>
								{
									if ( response.message ) {
										location.reload();
									}
								}
							})

							dialog.hide();

							clearWiggle();
						});
						// Hacks, Hacks and Hacks.
						var $cancel = dialog.get_close_btn();
						$cancel.click(() => {
							clearWiggle();
						});
						$cancel.html(__(`Cancel`));

						dialog.show();
					});
				});

				$icons.trigger('startRumble');
			}, DURATION_LONG_PRESS);
		});
		frappe.desktop.wrapper.on('mouseup mouseleave', '.app-icon', () => {
			clearTimeout(timer_id);
		});

		// also stop wiggling if clicked elsewhere.
		$('body').click((event) => {
			if ( wiggling ) {
				const $target = $(event.target);
				// our target shouldn't be .app-icons or .close
				const $parent = $target.parents('.case-wrapper');
				if ( $parent.length == 0 )
					clearWiggle();
			}
		});
		// end wiggle
	},

	open_module: function(parent) {
		var link = parent.attr("data-link");
		if(link) {
			if(link.indexOf('javascript:')===0) {
				eval(link.substr(11));
			} else if(link.substr(0, 1)==="/" || link.substr(0, 4)==="http") {
				window.open(link, "_blank");
			} else {
				frappe.set_route(link);
			}
			return false;
		} else {
			var module = frappe.get_module(parent.attr("data-name"));
			if (module && module.onclick) {
				module.onclick();
				return false;
			}
		}
	},

	make_sortable: function() {
		if (frappe.dom.is_touchscreen() || frappe.list_desktop) {
			return;
		}

		new Sortable($("#icon-grid").get(0), {
			onUpdate: function(event) {
				var new_order = [];
				$("#icon-grid .case-wrapper").each(function(i, e) {
					new_order.push($(this).attr("data-name"));
				});

				frappe.call({
					method: 'frappe.desk.doctype.desktop_icon.desktop_icon.set_order',
					args: {
						'new_order': new_order,
						'user': frappe.session.user
					},
					quiet: true
				});
			}
		});
	},

	set_background: function() {
		frappe.ui.set_user_background(frappe.boot.user.background_image, null,
			frappe.boot.user.background_style);
	},

	show_pending_notifications: function() {
		var modules_list = frappe.get_desktop_icons();
		for (var i=0, l=modules_list.length; i < l; i++) {
			var module = modules_list[i];

			var module_doctypes = frappe.boot.notification_info.module_doctypes[module.module_name];

			var sum = 0;
			if(module_doctypes) {
				if(frappe.boot.notification_info.open_count_doctype) {
					// sum all doctypes for a module
					for (var j=0, k=module_doctypes.length; j < k; j++) {
						var doctype = module_doctypes[j];
						sum += (frappe.boot.notification_info.open_count_doctype[doctype] || 0);
					}
				}
			} else if(frappe.boot.notification_info.open_count_doctype
				&& frappe.boot.notification_info.open_count_doctype[module.module_name]!=null) {
				// notification count explicitly for doctype
				sum = frappe.boot.notification_info.open_count_doctype[module.module_name];

			} else if(frappe.boot.notification_info.open_count_module
				&& frappe.boot.notification_info.open_count_module[module.module_name]!=null) {
				// notification count explicitly for module
				sum = frappe.boot.notification_info.open_count_module[module.module_name];
			}

			// if module found
			if(module._id.indexOf('/')===-1) {
				var notifier = $(".module-count-" + module._id);
				if(notifier.length) {
					notifier.toggle(sum ? true : false);
					var circle = notifier.find(".circle-text");
					var text = sum || '';
					if(text > 20) {
						text = '20+';
					}

					if(circle.length) {
						circle.html(text);
					} else {
						notifier.html(text);
					}
				}
			}
		}
	}
});
<commit_msg>[minor] wiggling needs to be falsified when cleared<commit_after>frappe.provide('frappe.desktop');

frappe.pages['desktop'].on_page_load = function(wrapper) {

	// load desktop
	if(!frappe.list_desktop) {
		frappe.desktop.set_background();
	}
	frappe.desktop.refresh(wrapper);
};

frappe.pages['desktop'].on_page_show = function(wrapper) {
	if(frappe.list_desktop) {
		$("body").attr("data-route", "list-desktop");
	}
};

$.extend(frappe.desktop, {
	refresh: function(wrapper) {
		if (wrapper) {
			this.wrapper = $(wrapper);
		}

		this.render();
		this.make_sortable();
	},

	render: function() {
		var me = this;
		frappe.utils.set_title(__("Desktop"));

		var template = frappe.list_desktop ? "desktop_list_view" : "desktop_icon_grid";

		var all_icons = frappe.get_desktop_icons();
		var explore_icon = {
			module_name: 'Explore',
			label: 'Explore',
			_label: __('Explore'),
			_id: 'Explore',
			_doctype: '',
			icon: 'octicon octicon-telescope',
			color: '#7578f6',
			link: 'modules'
		};
		explore_icon.app_icon = frappe.ui.app_icon.get_html(explore_icon);
		all_icons.push(explore_icon);

		frappe.desktop.wrapper.html(frappe.render_template(template, {
			// all visible icons
			desktop_items: all_icons,
		}));

		frappe.desktop.setup_module_click();

		// notifications
		frappe.desktop.show_pending_notifications();
		$(document).on("notification-update", function() {
			me.show_pending_notifications();
		});

		$(document).trigger("desktop-render");

	},

	render_help_messages: function(help_messages) {
		var wrapper = frappe.desktop.wrapper.find('.help-message-wrapper');
		var $help_messages = wrapper.find('.help-messages');

		var set_current_message = function(idx) {
			idx = cint(idx);
			wrapper.current_message_idx = idx;
			wrapper.find('.left-arrow, .right-arrow').addClass('disabled');
			wrapper.find('.help-message-item').addClass('hidden');
			wrapper.find('[data-message-idx="'+idx+'"]').removeClass('hidden');
			if(idx > 0) {
				wrapper.find('.left-arrow').removeClass('disabled');
			}
			if(idx < help_messages.length - 1) {
				wrapper.find('.right-arrow').removeClass('disabled');
			}
		}

		if(help_messages) {
			wrapper.removeClass('hidden');
			help_messages.forEach(function(message, i) {
				var $message = $('

')
					.attr('data-message-idx', i)
					.html(frappe.render_template('desktop_help_message', message))
					.appendTo($help_messages);

			});

			set_current_message(0);

			wrapper.find('.close').on('click', function() {
				wrapper.addClass('hidden');
			});
		}

		wrapper.find('.left-arrow').on('click', function() {
			if(wrapper.current_message_idx) {
				set_current_message(wrapper.current_message_idx - 1);
			}
		})

		wrapper.find('.right-arrow').on('click', function() {
			if(help_messages.length > wrapper.current_message_idx + 1) {
				set_current_message(wrapper.current_message_idx + 1);
			}
		});

	},

	setup_module_click: function() {
		var wiggling = false; // wiggle, wiggle, wiggle.

		if(frappe.list_desktop) {
			frappe.desktop.wrapper.on("click", ".desktop-list-item", function() {
				frappe.desktop.open_module($(this));
			});
		} else {
			frappe.desktop.wrapper.on("click", ".app-icon", function() {
				if ( !wiggling ) {
					frappe.desktop.open_module($(this).parent());
				}
			});
		}
		frappe.desktop.wrapper.on("click", ".circle", function() {
			var doctype = $(this).attr('data-doctype');
			if(doctype) {
				frappe.ui.notifications.show_open_count_list(doctype);
			}
		});

		// Wiggle, Wiggle, Wiggle.
		const DURATION_LONG_PRESS = 1000;
		// lesser the antidode, more the wiggle (like your drunk uncle)
		// 75 seems good to replicate the iOS feels.
		const WIGGLE_ANTIDODE     = 75;

		var   timer_id      = 0;
		const $cases        = frappe.desktop.wrapper.find('.case-wrapper');
		const $icons        = frappe.desktop.wrapper.find('.app-icon');
		const $notis        = $(frappe.desktop.wrapper.find('.circle').toArray().filter((object) => {
			// This hack is so bad, I should punch myself.
			const doctype   = $(object).data('doctype');

			return doctype;
		}));

		const clearWiggle   = () => {
			const $closes   = $cases.find('.module-remove');
			$closes.hide();
			$notis.show();

			$icons.trigger('stopRumble');

			wiggling = false;
		};

		// initiate wiggling.
		$icons.jrumble({
			speed: WIGGLE_ANTIDODE // seems neat enough to match the iOS way
		});

		frappe.desktop.wrapper.on('mousedown', '.app-icon', () => {
			timer_id     = setTimeout(() => {
				wiggling = true;
				// hide all notifications.
				$notis.hide();

				$cases.each((i) => {
					const $case    = $($cases[i]);
					const template =
					`

									&times

					`;

					$case.append(template);
					const $close  = $case.find('.module-remove');
					const name    = $case.attr('title');
					$close.click(() => {
						// good enough to create dynamic dialogs?
						const dialog = new frappe.ui.Dialog({
							title: __(`Hide ${name}?`)
						});
						dialog.set_primary_action(__('Hide'), () => {
							frappe.call({
								method: 'frappe.desk.doctype.desktop_icon.desktop_icon.hide',
								args: { name: name },
								freeze: true,
								callback: (response) =>
								{
									if ( response.message ) {
										location.reload();
									}
								}
							})

							dialog.hide();

							clearWiggle();
						});
						// Hacks, Hacks and Hacks.
						var $cancel = dialog.get_close_btn();
						$cancel.click(() => {
							clearWiggle();
						});
						$cancel.html(__(`Cancel`));

						dialog.show();
					});
				});

				$icons.trigger('startRumble');
			}, DURATION_LONG_PRESS);
		});
		frappe.desktop.wrapper.on('mouseup mouseleave', '.app-icon', () => {
			clearTimeout(timer_id);
		});

		// also stop wiggling if clicked elsewhere.
		$('body').click((event) => {
			if ( wiggling ) {
				const $target = $(event.target);
				// our target shouldn't be .app-icons or .close
				const $parent = $target.parents('.case-wrapper');
				if ( $parent.length == 0 )
					clearWiggle();
			}
		});
		// end wiggle
	},

	open_module: function(parent) {
		var link = parent.attr("data-link");
		if(link) {
			if(link.indexOf('javascript:')===0) {
				eval(link.substr(11));
			} else if(link.substr(0, 1)==="/" || link.substr(0, 4)==="http") {
				window.open(link, "_blank");
			} else {
				frappe.set_route(link);
			}
			return false;
		} else {
			var module = frappe.get_module(parent.attr("data-name"));
			if (module && module.onclick) {
				module.onclick();
				return false;
			}
		}
	},

	make_sortable: function() {
		if (frappe.dom.is_touchscreen() || frappe.list_desktop) {
			return;
		}

		new Sortable($("#icon-grid").get(0), {
			onUpdate: function(event) {
				var new_order = [];
				$("#icon-grid .case-wrapper").each(function(i, e) {
					new_order.push($(this).attr("data-name"));
				});

				frappe.call({
					method: 'frappe.desk.doctype.desktop_icon.desktop_icon.set_order',
					args: {
						'new_order': new_order,
						'user': frappe.session.user
					},
					quiet: true
				});
			}
		});
	},

	set_background: function() {
		frappe.ui.set_user_background(frappe.boot.user.background_image, null,
			frappe.boot.user.background_style);
	},

	show_pending_notifications: function() {
		var modules_list = frappe.get_desktop_icons();
		for (var i=0, l=modules_list.length; i < l; i++) {
			var module = modules_list[i];

			var module_doctypes = frappe.boot.notification_info.module_doctypes[module.module_name];

			var sum = 0;
			if(module_doctypes) {
				if(frappe.boot.notification_info.open_count_doctype) {
					// sum all doctypes for a module
					for (var j=0, k=module_doctypes.length; j < k; j++) {
						var doctype = module_doctypes[j];
						sum += (frappe.boot.notification_info.open_count_doctype[doctype] || 0);
					}
				}
			} else if(frappe.boot.notification_info.open_count_doctype
				&& frappe.boot.notification_info.open_count_doctype[module.module_name]!=null) {
				// notification count explicitly for doctype
				sum = frappe.boot.notification_info.open_count_doctype[module.module_name];

			} else if(frappe.boot.notification_info.open_count_module
				&& frappe.boot.notification_info.open_count_module[module.module_name]!=null) {
				// notification count explicitly for module
				sum = frappe.boot.notification_info.open_count_module[module.module_name];
			}

			// if module found
			if(module._id.indexOf('/')===-1) {
				var notifier = $(".module-count-" + module._id);
				if(notifier.length) {
					notifier.toggle(sum ? true : false);
					var circle = notifier.find(".circle-text");
					var text = sum || '';
					if(text > 20) {
						text = '20+';
					}

					if(circle.length) {
						circle.html(text);
					} else {
						notifier.html(text);
					}
				}
			}
		}
	}
});
<|endoftext|>


<commit_before>/**
 * Story
 *
 * @module      ::  Model
 * @description ::  This model represent user story on taskboard. User stories are attached to project and
 *                  sprint. Sprint relation is not required, if sprintId is 0 or null current user story is
 *                  in project backlog.
 */
module.exports = {
    schema: true,
    attributes: {
        // Relation to Project model
        projectId: {
            type:       'integer',
            required:   true
        },
        // Relation to Sprint model
        sprintId: {
            type:       'integer',
            defaultsTo: 0,
            required:   true
        },
        // Relation to Milestone model
        milestoneId: {
            type:       'integer',
            defaultsTo: 0
        },
        // Relation to type model, note that this is just default type for story tasks
        typeId: {
            type:       'integer',
            defaultsTo: 0,
            required:   true
        },
        title: {
            type:       'string',
            required:   true,
            minLength:  5
        },
        description: {
            type:       'text',
            required:   true,
            minLength:  10
        },
        estimate: {
            type:       'integer',
            required:   true,
            defaultsTo: -1
        },
        priority: {
            type:       'integer',
            defaultsTo: 0
        },
        vfCase: {
            type:       'integer',
            defaultsTo: 0
        },
        isDone: {
            type:       'boolean',
            required:   true,
            defaultsTo: 0
        },

        estimateFormatted: function() {
            return (parseInt(this.estimate, 10) === -1) ? '???' : this.estimate;
        },

        objectTitle: function() {
            return this.title;
        }
    },

    // Lifecycle Callbacks

    /**
     * Before create callback. Basically we want to make sure that isDone bit is set to false
     * and calculate story priority according to same project and sprint stories.
     *
     * @param   {sails.model.story} values
     * @param   {Function}          cb
     */
    beforeCreate: function(values, cb) {
        values.isDone = false;

        Story
            .findOne({
                projectId: values.projectId,
                sprintId: values.sprintId
            })
            .sort('priority DESC')
            .done(function(error, story) {
                var priority;

                if (error) {
                    cb(error)
                } else if (!story) {
                    priority = 0;
                } else {
                    priority = story.priority + 1;
                }

                values.priority = priority;

                cb();
            });
    },

    /**
     * After create callback
     *
     * @param   {sails.model.story} values
     * @param   {Function}          cb
     */
    afterCreate: function(values, cb) {
        HistoryService.write('Story', values);

        cb();
    },

    /**
     * After update callback
     *
     * @param   {sails.model.story} values
     * @param   {Function}          cb
     */
    afterUpdate: function(values, cb) {
        HistoryService.write('Story', values);

        cb();
    },

    /**
     * Before destroy callback
     *
     * @param   {Object}    terms
     * @param   {Function}  cb
     */
    beforeDestroy: function(terms, cb) {
        Story
            .findOne(terms)
            .done(function(error, story) {
                HistoryService.remove('Story', story.id);

                cb();
            });
    }
};
<commit_msg>Updated Story object model.<commit_after>/**
 * Story
 *
 * @module      ::  Model
 * @description ::  This model represent user story on taskboard. User stories are attached to project and
 *                  sprint. Sprint relation is not required, if sprintId is 0 or null current user story is
 *                  in project backlog.
 */
module.exports = {
    schema: true,
    attributes: {
        // Relation to Project model
        projectId: {
            type:       'integer',
            required:   true
        },
        // Relation to Sprint model
        sprintId: {
            type:       'integer',
            defaultsTo: 0
        },
        // Relation to Milestone model
        milestoneId: {
            type:       'integer',
            defaultsTo: 0
        },
        // Relation to type model, note that this is just default type for story tasks
        typeId: {
            type:       'integer',
            defaultsTo: 0,
            required:   true
        },
        title: {
            type:       'string',
            required:   true,
            minLength:  5
        },
        description: {
            type:       'text',
            required:   true,
            minLength:  10
        },
        estimate: {
            type:       'integer',
            required:   true,
            defaultsTo: -1
        },
        priority: {
            type:       'integer',
            defaultsTo: 0
        },
        vfCase: {
            type:       'integer',
            defaultsTo: 0
        },
        isDone: {
            type:       'boolean',
            required:   true,
            defaultsTo: 0
        },

        estimateFormatted: function() {
            return (parseInt(this.estimate, 10) === -1) ? '???' : this.estimate;
        },

        objectTitle: function() {
            return this.title;
        }
    },

    // Lifecycle Callbacks

    /**
     * Before create callback. Basically we want to make sure that isDone bit is set to false
     * and calculate story priority according to same project and sprint stories.
     *
     * @param   {sails.model.story} values
     * @param   {Function}          cb
     */
    beforeCreate: function(values, cb) {
        values.isDone = false;

        Story
            .findOne({
                projectId: values.projectId,
                sprintId: values.sprintId
            })
            .sort('priority DESC')
            .done(function(error, story) {
                var priority;

                if (error) {
                    cb(error)
                } else if (!story) {
                    priority = 0;
                } else {
                    priority = story.priority + 1;
                }

                values.priority = priority;

                cb();
            });
    },

    /**
     * After create callback
     *
     * @param   {sails.model.story} values
     * @param   {Function}          cb
     */
    afterCreate: function(values, cb) {
        HistoryService.write('Story', values);

        cb();
    },

    /**
     * After update callback
     *
     * @param   {sails.model.story} values
     * @param   {Function}          cb
     */
    afterUpdate: function(values, cb) {
        HistoryService.write('Story', values);

        cb();
    },

    /**
     * Before destroy callback
     *
     * @param   {Object}    terms
     * @param   {Function}  cb
     */
    beforeDestroy: function(terms, cb) {
        Story
            .findOne(terms)
            .done(function(error, story) {
                HistoryService.remove('Story', story.id);

                cb();
            });
    }
};
<|endoftext|>


<commit_before>var defaultOptions = {
  protocol: 'http://',
  /*host: 'localhost',
  port: 8091,*/
  redirector: 'http://redirector.flotype.com',
  reconnect: true,
  log: 2,
  tcp: false
};

// if node
var util = require('./util.js');

var Connection = require('./connection.js').Connection;
var Serializer = require('./serializer.js');
var Ref = require('./ref.js');

util.extend(defaultOptions, {
  /*port: 8090,*/
  tcp: true
});

// end node

function Bridge(options) {

  var self = this;

  // Initialize system call service
  var system = {
    hook_channel_handler: function(name, handler, callback){
      self.children['channel:' + name] = self.children[handler._getRef()._pathchain[2]];
      if (callback) {
        var ref = self.getPathObj(['channel', name, 'channel:' + name]);
        ref._setOps(util.findKeys(self.children['channel:' + name]));
        callback.call( ref, name );
      }
    },
    getservice: function(name, callback){
      if (util.hasProp(self.children, name)) {
        callback.call(self.children[name]);
      } else {
        callback.call(null, "Cannot find service " + name);
      }
    },
    remoteError: function(msg) {
      util.warn(msg);
      self.emit('remoteError', [msg]);
    }
  };

  // Set configuration options
  this.options = util.extend(defaultOptions, options);

  // Set logging level
  util.setLogLevel(this.options.log);

  // Contains references to shared references
  this.children = {system: system};

  // Indicate whether connected
  this.connected = false;

  // Communication layer
  this.connection = new Connection(this);

  // Store event handlers
  this._events = {};
}

// Event emitter functions
Bridge.prototype.on = function (name, fn) {
  if (!(util.hasProp(this._events, name))) {
    this._events[name] = [];
  }
  this._events[name].push(fn);
  return this;
};

Bridge.prototype.emit = function (name, args) {
  if (util.hasProp(this._events, name)) {
    var events = this._events[name].slice(0);
    for (var i = 0, ii = events.length; i < ii; i++) {
      events[i].apply(this, args === undefined ? [] : args);
    }
  }
  return this;
};

Bridge.prototype.removeEvent = function (name, fn) {
  if (util.hasProp(this._events, name)) {
    for (var a = 0, l = this._events[name].length; a < l; a++) {
      if (this._events[name][a] === fn) {
        this._events[name].splice(a, 1);
      }
    }
  }
  return this;
};

Bridge.prototype.onReady = function() {
  util.info('Handshake complete');
  if(!this.connected) {
    this.connected = true;
    this.emit('ready');
  }
};

Bridge.prototype.onMessage = function(message) {
  Serializer.unserialize(this, message);
  var unser = message;
  var destination = unser.destination;
  // util.info('DECODED: ', unser.args );
  if (!destination) {
    util.warn('No destination in message', unser);
    return;
  }
  var pathchain = unser.destination._pathchain;
  var args = unser.args;

  this.execute(pathchain, args);
};

Bridge.prototype.execute = function(pathchain, args) {
  var obj = this.children[pathchain[2]];
  var func = obj[pathchain[3]];

  if (func) {
    func.apply( obj, args );
  } else {
    // TODO: Throw exception
    util.warn('No Func nor Default Handler for', pathchain);
  }
};

Bridge.prototype.publishService = function(name, service, callback) {

  if(name === "system") {
    util.error("Invalid service name: " + name);
    return;
  }

  var self = this;

  if ( (!service._getRef) || (util.typeOf(service._getRef) !== 'function') ) {
    service._getRef = function() { return self.getPathObj( ['named', name, name] ); };
    this.connection.publishService(name, callback);
  } else {
    util.error("Service can't be renamed! " + name + ' old ' +  service._getRef().getLocalName() );
    return;
  }
  this.children[name] = service;
  return service._getRef();
};

Bridge.prototype.createCallback = function(service) {
  var self = this;
  var name;
  var ref;
  if ( (!service._getRef) || (util.typeOf(service._getRef) !== 'function') ) {
    name = util.generateGuid();
    ref = self.getPathObj( ['client', self.getClientId(), name] );
    this.children[name] = service;
  } else {
    ref = service._getRef();
  }
  return ref;
};

Bridge.prototype.joinChannel = function(name, handler, callback) {
  this.connection.joinChannel(name, handler, callback);
};

Bridge.prototype.leaveChannel = function(name, handler, callback) {
  this.connection.leaveChannel(name, handler, callback);
};

Bridge.prototype.send = function(args, destination) {
  this.connection.send(args, destination);
};

Bridge.prototype.getPathObj = function(pathchain) {
  return new Ref(this, pathchain);
};

Bridge.prototype.getRootRef = function() {
  return this.getPathObj(['client', this.getClientId()]);
};

Bridge.prototype.get = function(pathStr)  {
  var pathchain = pathStr.split('.');
  return this.getPathObj(pathchain, true);
};

/* Public APIs */
Bridge.prototype.ready = function(func) {
  if(!this.connected) {
    this.on('ready', func);
  } else {
    func();
  }
};

Bridge.prototype.getClientId = function() {
  return this.connection.clientId;
};

Bridge.prototype.getService = function(name, callback) {
  this.connection.getService(name, callback);
};

Bridge.prototype.getChannel = function(name, callback) {
  this.connection.getChannel(name, callback);
};

// if node

exports.Bridge = Bridge;
// end node
<commit_msg>one more get<commit_after>var defaultOptions = {
  protocol: 'http://',
  /*host: 'localhost',
  port: 8091,*/
  redirector: 'http://redirector.flotype.com',
  reconnect: true,
  log: 2,
  tcp: false
};

// if node
var util = require('./util.js');

var Connection = require('./connection.js').Connection;
var Serializer = require('./serializer.js');
var Ref = require('./ref.js');

util.extend(defaultOptions, {
  /*port: 8090,*/
  tcp: true
});

// end node

function Bridge(options) {

  var self = this;

  // Initialize system call service
  var system = {
    hook_channel_handler: function(name, handler, callback){
      self.children['channel:' + name] = self.children[handler._getRef()._pathchain[2]];
      if (callback) {
        var ref = self.getPathObj(['channel', name, 'channel:' + name]);
        ref._setOps(util.findKeys(self.children['channel:' + name]));
        callback.call( ref, name );
      }
    },
    getservice: function(name, callback){
      if (util.hasProp(self.children, name)) {
        callback.call(self.children[name]);
      } else {
        callback.call(null, "Cannot find service " + name);
      }
    },
    remoteError: function(msg) {
      util.warn(msg);
      self.emit('remoteError', [msg]);
    }
  };

  // Set configuration options
  this.options = util.extend(defaultOptions, options);

  // Set logging level
  util.setLogLevel(this.options.log);

  // Contains references to shared references
  this.children = {system: system};

  // Indicate whether connected
  this.connected = false;

  // Communication layer
  this.connection = new Connection(this);

  // Store event handlers
  this._events = {};
}

// Event emitter functions
Bridge.prototype.on = function (name, fn) {
  if (!(util.hasProp(this._events, name))) {
    this._events[name] = [];
  }
  this._events[name].push(fn);
  return this;
};

Bridge.prototype.emit = function (name, args) {
  if (util.hasProp(this._events, name)) {
    var events = this._events[name].slice(0);
    for (var i = 0, ii = events.length; i < ii; i++) {
      events[i].apply(this, args === undefined ? [] : args);
    }
  }
  return this;
};

Bridge.prototype.removeEvent = function (name, fn) {
  if (util.hasProp(this._events, name)) {
    for (var a = 0, l = this._events[name].length; a < l; a++) {
      if (this._events[name][a] === fn) {
        this._events[name].splice(a, 1);
      }
    }
  }
  return this;
};

Bridge.prototype.onReady = function() {
  util.info('Handshake complete');
  if(!this.connected) {
    this.connected = true;
    this.emit('ready');
  }
};

Bridge.prototype.onMessage = function(message) {
  Serializer.unserialize(this, message);
  var unser = message;
  var destination = unser.destination;
  // util.info('DECODED: ', unser.args );
  if (!destination) {
    util.warn('No destination in message', unser);
    return;
  }
  var pathchain = unser.destination._pathchain;
  var args = unser.args;

  this.execute(pathchain, args);
};

Bridge.prototype.execute = function(pathchain, args) {
  var obj = this.children[pathchain[2]];
  var func = obj[pathchain[3]];

  if (func) {
    func.apply( obj, args );
  } else {
    // TODO: Throw exception
    util.warn('No Func nor Default Handler for', pathchain);
  }
};

Bridge.prototype.publishService = function(name, service, callback) {

  if(name === "system") {
    util.error("Invalid service name: " + name);
    return;
  }

  var self = this;

  if ( (!service._getRef) || (util.typeOf(service._getRef) !== 'function') ) {
    service._getRef = function() { return self.getPathObj( ['named', name, name] ); };
    this.connection.publishService(name, callback);
  } else {
    util.error("Service can't be renamed! " + name + ' old ' +  service._getRef().getLocalName() );
    return;
  }
  this.children[name] = service;
  return service._getRef();
};

Bridge.prototype.createCallback = function(service) {
  var self = this;
  var name;
  var ref;
  if ( (!service._getRef) || (util.typeOf(service._getRef) !== 'function') ) {
    name = util.generateGuid();
    ref = self.getPathObj( ['client', self.getClientId(), name] );
    this.children[name] = service;
  } else {
    ref = service._getRef();
  }
  return ref;
};

Bridge.prototype.joinChannel = function(name, handler, callback) {
  this.connection.joinChannel(name, handler, callback);
};

Bridge.prototype.leaveChannel = function(name, handler, callback) {
  this.connection.leaveChannel(name, handler, callback);
};

Bridge.prototype.send = function(args, destination) {
  this.connection.send(args, destination);
};

Bridge.prototype.getPathObj = function(pathchain) {
  return new Ref(this, pathchain);
};

Bridge.prototype.getRootRef = function() {
  return this.getPathObj(['client', this.getClientId()]);
};

Bridge.prototype._get = function(pathStr)  {
  var pathchain = pathStr.split('.');
  return this.getPathObj(pathchain, true);
};

/* Public APIs */
Bridge.prototype.ready = function(func) {
  if(!this.connected) {
    this.on('ready', func);
  } else {
    func();
  }
};

Bridge.prototype.getClientId = function() {
  return this.connection.clientId;
};

Bridge.prototype.getService = function(name, callback) {
  this.connection.getService(name, callback);
};

Bridge.prototype.getChannel = function(name, callback) {
  this.connection.getChannel(name, callback);
};

// if node

exports.Bridge = Bridge;
// end node
<|endoftext|>


<commit_before>require.config({
  baseUrl: 'modules',
  packages: [
    'application',
    'cs_account',
    'cs_common',
    'cs_session',
    'users'
  ],
  paths: {
    angular: '../components/angular/angular',
    jquery: '../components/jquery/jquery',
    underscore: '../components/underscore/underscore',
    ngCookies: '../components/angular-cookies/angular-cookies',
    ngResource: '../components/angular-resource/angular-resource',
    ngRoute: '../components/angular-route/angular-route',
    ngSanitize: '../components/angular-sanitize/angular-sanitize',
    'http-auth-interceptor': '../components/angular-http-auth/src/http-auth-interceptor',
    bootstrap: '../scripts/bootstrap'
  },
  shim: {
    angular: {
      exports: 'angular'
    },
    ngCookies: {
      deps: ['angular']
    },
    ngResource: {
      deps: ['angular']
    },
    ngRoute: {
      deps: ['angular']
    },
    ngSanitize: {
      deps: ['angular']
    },
    'http-auth-interceptor': {
      deps: ['angular']
    },
    bootstrap: {
      deps: ['jquery']
    },
    underscore: {
      exports: '_'
    }
  }
});

require([
  'angular',
  'ngRoute',
  'ngResource',
  'ngSanitize',
  'http-auth-interceptor',
  'bootstrap',

  // Init
  'application',

], function (angular) {
  'use strict';

  angular.element(document).ready(function () {
    angular.bootstrap(document, ['app']);
  });
});
<commit_msg>refactor(require): Main js file cleaned up for 1.2<commit_after>require.config({
  baseUrl: 'modules',
  packages: [
    'app',
    'cs_common',
    'cs_messenger',
    'cs_modal',
    'auth',
    'roles'
  ],
  paths: {
    angular:                  '../components/angular/angular',
    ngCookies:                '../components/angular-cookies/angular-cookies',
    ngResource:               '../components/angular-resource/angular-resource',
    ngRoute:                  '../components/angular-route/angular-route',
    ngSanitize:               '../components/angular-sanitize/angular-sanitize',
    ngTable:                  '../components/ng-table/ng-table',
    ngTableResizableColumns:  '../components/ng-table-resizable-columns/ng-table-resizable-columns.src',
    ngUi:                     '../components/angular-ui/build/angular-ui',
    ngUiBootstrap:            '../components/angular-bootstrap/ui-bootstrap-tpls',
    httpAuthInterceptor:      '../components/angular-http-auth/src/http-auth-interceptor',
    bootstrap:                '../scripts/bootstrap',
    jquery:                   '../components/jquery/jquery',
    jqueryMinicolors:         '../components/jquery-minicolors/jquery.minicolors',
    underscore:               '../components/underscore/underscore',
    selectn:                  '../components/selectn/selectn',
    inflection:               '../components/inflection/lib/inflection',
    select2:                  '../components/select2/select2',
    'ui.select2':             '../components/angular-ui-select2/src/select2',
    moment:                   '../components/momentjs/moment'
  },
  shim: {
    angular: {
      deps:     [ 'jquery' ],
      exports:  'angular'
    },
    ngCookies: {
      deps:     [ 'angular' ]
    },
    ngResource: {
      deps:     [ 'angular' ]
    },
    ngRoute: {
      deps:     [ 'angular' ]
    },
    ngSanitize: {
      deps:     [ 'angular' ]
    },
    ngTable: {
      deps:     [ 'angular' ],
      exports:  'ngTable'
    },
    ngTableResizableColumns: {
      deps:     [ 'angular' ],
      exports:  'ngTableResizableColumns'
    },
    ngUi: {
      deps:     [ 'angular' ]
    },
    ngUiBootstrap: {
      deps:     [ 'angular' ]
    },
    httpAuthInterceptor: {
      deps:     [ 'angular' ]
    },
    bootstrap: {
      deps:     [ 'jquery' ]
    },
    underscore: {
      exports:  '_'
    },
    selectn: {
      deps:     [ 'bootstrap' ]
    },
    inflection: {
      exports:  'inflection'
    },
    select2: {
      deps:     [ 'jquery' ]
    },
    'ui.select2': {
      deps:     [ 'angular', 'select2' ]
    },
    moment: {
      exports:  'moment'
    },
    jqueryMinicolors: {
      deps:     [ 'jquery' ]
    }
  },
  waitSeconds: 15
});

require([
  'angular',
  'bootstrap',
  'ngUi',
  'ngUiBootstrap',
  'ngRoute',
  'ngResource',
  'ngSanitize',
  'ngTable',
  'ngTableResizableColumns',
  'httpAuthInterceptor',

  'ui.select2',
  'selectn',
  'inflection',

  // CleverStack modules
  'cs_common',
  'cs_messenger',
  'cs_modal',
  'auth',
  'roles',

  // Main app module
  'app',

  // Custom modules

], function( angular ) {
  'use strict';

  angular.element( document ).ready( function() {
    angular.bootstrap( document, [ 'app' ] );
  });
});
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Inject this script on any page to measure framerate as the page is scrolled
// from top to bottom.
//
// Usage:
//   1. Define a callback that takes the results array as a parameter.
//   2. To start the test, call new __ScrollTest(callback).
//   3a. When the test is complete, the callback will be called.
//   3b. If no callback is specified, the results are sent to the console.

(function() {
  var getTimeMs = (function() {
    if (window.performance)
      return (performance.now       ||
              performance.mozNow    ||
              performance.msNow     ||
              performance.oNow      ||
              performance.webkitNow).bind(window.performance);
    else
      return function() { return new Date().getTime(); };
  })();

  var requestAnimationFrame = (function() {
    return window.requestAnimationFrame       ||
           window.webkitRequestAnimationFrame ||
           window.mozRequestAnimationFrame    ||
           window.oRequestAnimationFrame      ||
           window.msRequestAnimationFrame     ||
           function(callback) {
             window.setTimeout(callback, 1000 / 60);
           };
  })().bind(window);

  function GpuBenchmarkingScrollStats() {
    this.initialStats_ = this.getRenderingStats_();
  }

  GpuBenchmarkingScrollStats.prototype.getResult = function() {
    var stats = this.getRenderingStats_();
    for (var key in stats)
      stats[key] -= this.initialStats_[key];
    return stats;
  };

  GpuBenchmarkingScrollStats.prototype.getRenderingStats_ = function() {
    var stats = chrome.gpuBenchmarking.renderingStats();
    stats.totalTimeInSeconds = getTimeMs() / 1000;
    return stats;
  };

  function RafScrollStats(timestamp) {
    this.frameTimes_ = [timestamp];
    this.recording_ = true;
    requestAnimationFrame(this.processStep_.bind(this));
  }

  RafScrollStats.prototype.getResult = function() {
    this.recording_ = false;

    // Fill in the result object.
    var result = {};
    result.numAnimationFrames = this.frameTimes_.length - 1;
    result.droppedFrameCount = this.getDroppedFrameCount_(this.frameTimes_);
    result.totalTimeInSeconds = (this.frameTimes_[this.frameTimes_.length - 1] -
        this.frameTimes_[0]) / 1000;
    return result;
  };

  RafScrollStats.prototype.processStep_ = function(timestamp) {
    if (!this.recording_)
      return;

    this.frameTimes_.push(timestamp);
    requestAnimationFrame(this.processStep_.bind(this));
  };

  RafScrollStats.prototype.getDroppedFrameCount_ = function(frameTimes) {
    var droppedFrameCount = 0;
    for (var i = 1; i < frameTimes.length; i++) {
      var frameTime = frameTimes[i] - frameTimes[i-1];
      if (frameTime > 1000 / 55)
        droppedFrameCount++;
    }
    return droppedFrameCount;
  };

  // In this class, a "step" is when the page is being scrolled.
  // i.e. startScroll -> startStep -> scroll -> processStep ->
  //                  -> startStep -> scroll -> processStep -> endScroll
  function ScrollTest(callback, opt_isGmailTest) {
    var self = this;

    this.TOTAL_ITERATIONS_ = 2;
    this.SCROLL_DELTA_ = 100;

    this.callback_ = callback;
    this.isGmailTest_ = opt_isGmailTest;
    this.iteration_ = 0;

    this.results_ = []

    if (this.isGmailTest_) {
      gmonkey.load('2.0', function(api) {
          self.start_(api.getScrollableElement());
      });
    } else {
      if (document.readyState == 'complete')
        this.start_();
      else
        window.addEventListener('load', function() { self.start_(); });
    }
  }

  ScrollTest.prototype.scroll_ = function(x, y) {
    if (this.isGmailTest_)
      this.element_.scrollByLines(1);
    else
      window.scrollBy(x, y);
  };

  ScrollTest.prototype.start_ = function(opt_element) {
    // Assign this.element_ here instead of constructor, because the constructor
    // ensures this method will be called after the document is loaded.
    this.element_ = opt_element || document.body;
    requestAnimationFrame(this.startScroll_.bind(this));
  };

  ScrollTest.prototype.startScroll_ = function(timestamp) {
    this.element_.scrollTop = 0;
    if (window.chrome && chrome.gpuBenchmarking)
      this.scrollStats_ = new GpuBenchmarkingScrollStats();
    else
      this.scrollStats_ = new RafScrollStats(timestamp);
    this.startStep_();
  };

  ScrollTest.prototype.startStep_ = function() {
    this.scroll_(0, this.SCROLL_DELTA_);
    requestAnimationFrame(this.processStep_.bind(this));
  };

  ScrollTest.prototype.endScroll_ = function() {
    this.results_.push(this.scrollStats_.getResult());
    this.iteration_++;
  };

  ScrollTest.prototype.processStep_ = function(timestamp) {
    // clientHeight is "special" for the body element.
    if (this.element_ == document.body)
      var clientHeight = window.innerHeight;
    else
      var clientHeight = this.element_.clientHeight;
    var isScrollComplete =
        this.element_.scrollTop + clientHeight >= this.element_.scrollHeight;

    if (!isScrollComplete) {
      this.startStep_();
      return;
    }

    this.endScroll_();

    var isTestComplete = this.iteration_ >= this.TOTAL_ITERATIONS_;
    if (!isTestComplete) {
      requestAnimationFrame(this.startScroll_.bind(this));
      return;
    }

    // Send results.
    if (this.callback_)
      this.callback_(this.results_);
    else
      console.log(this.results_);
  };

  window.__ScrollTest = ScrollTest;
})();
<commit_msg>Make scroll.js aware of beginSmoothScroll<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Inject this script on any page to measure framerate as the page is scrolled
// from top to bottom.
//
// Usage:
//   1. Define a callback that takes the results array as a parameter.
//   2. To start the test, call new __ScrollTest(callback).
//   3a. When the test is complete, the callback will be called.
//   3b. If no callback is specified, the results are sent to the console.

(function() {
  var getTimeMs = (function() {
    if (window.performance)
      return (performance.now       ||
              performance.mozNow    ||
              performance.msNow     ||
              performance.oNow      ||
              performance.webkitNow).bind(window.performance);
    else
      return function() { return new Date().getTime(); };
  })();

  var requestAnimationFrame = (function() {
    return window.requestAnimationFrame       ||
           window.webkitRequestAnimationFrame ||
           window.mozRequestAnimationFrame    ||
           window.oRequestAnimationFrame      ||
           window.msRequestAnimationFrame     ||
           function(callback) {
             window.setTimeout(callback, 1000 / 60);
           };
  })().bind(window);

  /**
   * Scrolls a given element down a certain amount to emulate user scrolling.
   * Uses smooth scrolling capabilities provided by the platform, if available.
   * @constructor
   */
  function SmoothScrollDownGesture(opt_element, opt_isGmailTest) {
    this.element_ = opt_element || document.body;
    this.isGmailTest_ = opt_isGmailTest;
  };

  SmoothScrollDownGesture.prototype.start = function(callback) {
    this.callback_ = callback;
    if (chrome &&
        chrome.gpuBenchmarking &&
        chrome.gpuBenchmarking.beginSmoothScrollDown) {
      chrome.gpuBenchmarking.beginSmoothScrollDown(true, function() {
        callback();
      });
      return;
    }

    if (this.isGmailTest_) {
      this.element_.scrollByLines(1);
      requestAnimationFrame(callback);
      return;
    }

    var SCROLL_DELTA = 100;
    this.element_.scrollTop += SCROLL_DELTA;
    requestAnimationFrame(callback);
  }

  /**
   * Tracks rendering performance using the gpuBenchmarking.renderingStats API.
   * @constructor
   */
  function GpuBenchmarkingRenderingStats() {
  }

  GpuBenchmarkingRenderingStats.prototype.start = function() {
    this.initialStats_ = this.getRenderingStats_();
  }
  GpuBenchmarkingRenderingStats.prototype.stop = function() {
    this.finalStats_ = this.getRenderingStats_();
  }

  GpuBenchmarkingRenderingStats.prototype.getResult = function() {
    if (!this.initialStats_)
      throw new Error("Start not called.");

    if (!this.finalStats_)
      throw new Error("Stop was not called.");

    var stats = this.finalStats_;
    for (var key in stats)
      stats[key] -= this.initialStats_[key];
    return stats;
  };

  GpuBenchmarkingRenderingStats.prototype.getRenderingStats_ = function() {
    var stats = chrome.gpuBenchmarking.renderingStats();
    stats.totalTimeInSeconds = getTimeMs() / 1000;
    return stats;
  };

  /**
   * Tracks rendering performance using requestAnimationFrame.
   * @constructor
   */
  function RafRenderingStats() {
    this.recording_ = false;
    this.frameTimes_ = [];
  }

  RafRenderingStats.prototype.start = function() {
    if (this.recording_)
      throw new Error("Already started.");
    this.recording_ = true;
    requestAnimationFrame(this.recordFrameTime_.bind(this));
  }

  RafRenderingStats.prototype.stop = function() {
    this.recording_ = false;
  }

  RafRenderingStats.prototype.getResult = function() {
    var result = {};
    result.numAnimationFrames = this.frameTimes_.length - 1;
    result.droppedFrameCount = this.getDroppedFrameCount_(this.frameTimes_);
    result.totalTimeInSeconds = (this.frameTimes_[this.frameTimes_.length - 1] -
        this.frameTimes_[0]) / 1000;
    return result;
  };

  RafRenderingStats.prototype.recordFrameTime_ = function(timestamp) {
    if (!this.recording_)
      return;

    this.frameTimes_.push(timestamp);
    requestAnimationFrame(this.recordFrameTime_.bind(this));
  };

  RafRenderingStats.prototype.getDroppedFrameCount_ = function(frameTimes) {
    var droppedFrameCount = 0;
    for (var i = 1; i < frameTimes.length; i++) {
      var frameTime = frameTimes[i] - frameTimes[i-1];
      if (frameTime > 1000 / 55)
        droppedFrameCount++;
    }
    return droppedFrameCount;
  };

  // This class scrolls a page from the top to the bottom a given number of
  // times.
  //
  // Each full transit of the page is called a "pass."
  //
  // The page is scrolled down by a set of scroll gestures. These gestures
  // correspond to a reading gesture on that platform.
  //
  // i.e. for TOTAL_ITERATIONS_ = 2, we do
  // start_ -> startPass_ -> ...scrolling... -> onGestureComplete_ ->
  //        -> startPass_ -> .. scrolling... -> onGestureComplete_ -> callback_
  //
  // TODO(nduca): This test starts in its constructor. That is strange. We
  // should change it to start explicitly.
  function ScrollTest(opt_callback, opt_isGmailTest) {
    var self = this;

    this.TOTAL_ITERATIONS_ = 2;

    this.callback_ = opt_callback;
    this.isGmailTest_ = opt_isGmailTest;
    this.iteration_ = 0;

    this.results_ = []

    if (this.isGmailTest_) {
      gmonkey.load('2.0', function(api) {
          self.start_(api.getScrollableElement());
      });
    } else {
      if (document.readyState == 'complete')
        this.start_();
      else
        window.addEventListener('load', function() { self.start_(); });
    }
  }

  ScrollTest.prototype.start_ = function(opt_element) {
    // Assign this.element_ here instead of constructor, because the constructor
    // ensures this method will be called after the document is loaded.
    this.element_ = opt_element || document.body;
    requestAnimationFrame(this.startPass_.bind(this));
  };

  ScrollTest.prototype.startPass_ = function() {
    this.element_.scrollTop = 0;
    if (window.chrome && chrome.gpuBenchmarking)
      this.renderingStats_ = new GpuBenchmarkingRenderingStats();
    else
      this.renderingStats_ = new RafRenderingStats();
    this.renderingStats_.start();

    this.gesture_ = new SmoothScrollDownGesture(this.element_,
                                                this.isGmailTest_);
    this.gesture_.start(this.onGestureComplete_.bind(this));
  };

  ScrollTest.prototype.onGestureComplete_ = function(timestamp) {
    // clientHeight is "special" for the body element.
    var clientHeight;
    if (this.element_ == document.body)
      clientHeight = window.innerHeight;
    else
      clientHeight = this.element_.clientHeight;

    var isPassComplete =
        this.element_.scrollTop + clientHeight >= this.element_.scrollHeight;

    if (!isPassComplete) {
      this.gesture_.start(this.onGestureComplete_.bind(this));
      return;
    }

    this.endPass_();

    var isTestComplete = this.iteration_ >= this.TOTAL_ITERATIONS_;
    if (!isTestComplete) {
      this.startPass_();
      return;
    }

    // Send results.
    if (this.callback_)
      this.callback_(this.results_);
    else
      console.log(this.results_);
  };

  ScrollTest.prototype.endPass_ = function() {
    this.renderingStats_.stop();
    this.results_.push(this.renderingStats_.getResult());
    this.iteration_++;
  };

  window.__ScrollTest = ScrollTest;
})();
<|endoftext|>


<commit_before>'use strict';

var UNIT_REG = /\{\{\{unit(?=\s)([^}]+)\}\}\}/g;
var UNIT_NAME_REG = /\sname\s*=\s*"([^"]+)"/;
var UNIT_DATA_REG = /\sdata\s*=\s*"([^"]+)"/;

var path = require('path');
var util = require('./util');
var wrapError = require('./wrap-error');
var backendData = require('./backend-data');

/**
 * 组装 components
 */
module.exports = function (file, fisRet, fisSetting, fisOpt) {
    var content = file.getContent();
    // 组装 components
    content = content
        // 替换 Layout 中的 组件单元
        .replace(UNIT_REG, function (m) {
            var name, data;
            m = m.replace(UNIT_NAME_REG, function (mm, $$1) {
                name = $$1;
                return '';
            });
            // TODO: data 暂时还没有用到
            m = m.replace(UNIT_DATA_REG, function (mm, $$1) {
                data = $$1;
                return '';
            });
            if (name) {
                // 找到组件
                var cmpFilePath = path.join(file.subdirname, '../../', '/components/' + name + '/' + name + '.html')
                                      .replace(/\\/g, '/');
                var cmpFile = fisRet.src[cmpFilePath];
                if (cmpFile) {
                    m = cmpFile.getContent();
                } else {
                    m = wrapError('component ' + name + ' NOT found!');
                }
            } else {
                m = '<!-- ' + m + ' -->';
            }
            return m;
        });
    file.setContent(content);

    // 替换 BACKEND_DATA
    // scart 中直接使用 -p 参数表示打包
    var isPack = fisOpt.pack;
    // fis3 中使用 media === 'prod' 表示生产模式
    if (util.getType(fis.media) === util.FUNCTION) {
        var media = fis.media() || {};
        if (media._media === 'prod') {
            isPack = true;
        }
    }
    backendData(file, isPack);

    // 文件更新到 release 目录
    fisOpt.beforeCompile(file);
};
<commit_msg>component 支持多级目录<commit_after>'use strict';

var UNIT_REG = /\{\{\{unit(?=\s)([^}]+)\}\}\}/g;
var UNIT_NAME_REG = /\sname\s*=\s*"([^"]+)"/;
var UNIT_DATA_REG = /\sdata\s*=\s*"([^"]+)"/;

var path = require('path');
var util = require('./util');
var wrapError = require('./wrap-error');
var backendData = require('./backend-data');

/**
 * 组装 components
 */
module.exports = function (file, fisRet, fisSetting, fisOpt) {
    var content = file.getContent();
    // 组装 components
    content = content
        // 替换 Layout 中的 组件单元
        .replace(UNIT_REG, function (m) {
            var name, data;
            m = m.replace(UNIT_NAME_REG, function (mm, $$1) {
                name = $$1;
                return '';
            });
            // TODO: data 暂时还没有用到
            m = m.replace(UNIT_DATA_REG, function (mm, $$1) {
                data = $$1;
                return '';
            });
            if (name) {
                // 找到组件
                var htmlFile = name.split('/').pop();  // 支持多级目录
                var cmpFilePath = path.join(file.subdirname, '../../', '/components/' + name + '/' + htmlFile + '.html')
                                      .replace(/\\/g, '/');
                var cmpFile = fisRet.src[cmpFilePath];
                if (cmpFile) {
                    m = cmpFile.getContent();
                } else {
                    m = wrapError('component ' + name + ' NOT found!');
                }
            } else {
                m = '<!-- ' + m + ' -->';
            }
            return m;
        });
    file.setContent(content);

    // 替换 BACKEND_DATA
    // scart 中直接使用 -p 参数表示打包
    var isPack = fisOpt.pack;
    // fis3 中使用 media === 'prod' 表示生产模式
    if (util.getType(fis.media) === util.FUNCTION) {
        var media = fis.media() || {};
        if (media._media === 'prod') {
            isPack = true;
        }
    }
    backendData(file, isPack);

    // 文件更新到 release 目录
    fisOpt.beforeCompile(file);
};
<|endoftext|>


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

	var document = typeof window !== 'undefined' && typeof window.document !== 'undefined' ? window.document : {};
	var isCommonjs = typeof module !== 'undefined' && module.exports;
	var keyboardAllowed = typeof Element !== 'undefined' && 'ALLOW_KEYBOARD_INPUT' in Element;

	var fn = (function () {
		var val;

		var fnMap = [
			[
				'requestFullscreen',
				'exitFullscreen',
				'fullscreenElement',
				'fullscreenEnabled',
				'fullscreenchange',
				'fullscreenerror'
			],
			// New WebKit
			[
				'webkitRequestFullscreen',
				'webkitExitFullscreen',
				'webkitFullscreenElement',
				'webkitFullscreenEnabled',
				'webkitfullscreenchange',
				'webkitfullscreenerror'

			],
			// Old WebKit (Safari 5.1)
			[
				'webkitRequestFullScreen',
				'webkitCancelFullScreen',
				'webkitCurrentFullScreenElement',
				'webkitCancelFullScreen',
				'webkitfullscreenchange',
				'webkitfullscreenerror'

			],
			[
				'mozRequestFullScreen',
				'mozCancelFullScreen',
				'mozFullScreenElement',
				'mozFullScreenEnabled',
				'mozfullscreenchange',
				'mozfullscreenerror'
			],
			[
				'msRequestFullscreen',
				'msExitFullscreen',
				'msFullscreenElement',
				'msFullscreenEnabled',
				'MSFullscreenChange',
				'MSFullscreenError'
			]
		];

		var i = 0;
		var l = fnMap.length;
		var ret = {};

		for (; i < l; i++) {
			val = fnMap[i];
			if (val && val[1] in document) {
				for (i = 0; i < val.length; i++) {
					ret[fnMap[0][i]] = val[i];
				}
				return ret;
			}
		}

		return false;
	})();

	var eventNameMap = {
		change: fn.fullscreenchange,
		error: fn.fullscreenerror
	};

	var screenfull = {
		request: function (elem) {
			var request = fn.requestFullscreen;

			elem = elem || document.documentElement;

			// Work around Safari 5.1 bug: reports support for
			// keyboard in fullscreen even though it doesn't.
			// Browser sniffing, since the alternative with
			// setTimeout is even worse.
			if (/5\.1[.\d]* Safari/.test(navigator.userAgent)) {
				elem[request]();
			} else {
				elem[request](keyboardAllowed && Element.ALLOW_KEYBOARD_INPUT);
			}
		},
		exit: function () {
			document[fn.exitFullscreen]();
		},
		toggle: function (elem) {
			if (this.isFullscreen) {
				this.exit();
			} else {
				this.request(elem);
			}
		},
		onchange: function (callback) {
			this.on('change', callback);
		},
		onerror: function (callback) {
			this.on('error', callback);
		},
		on: function (event, callback) {
			var eventName = eventNameMap[event];
			if (eventName) {
				document.addEventListener(eventName, callback, false);
			}
		},
		off: function (event, callback) {
			var eventName = eventNameMap[event];
			if (eventName) {
				document.off(eventName, callback, false);
			}
		},
		raw: fn
	};

	if (!fn) {
		if (isCommonjs) {
			module.exports = false;
		} else {
			window.screenfull = false;
		}

		return;
	}

	Object.defineProperties(screenfull, {
		isFullscreen: {
			get: function () {
				return Boolean(document[fn.fullscreenElement]);
			}
		},
		element: {
			enumerable: true,
			get: function () {
				return document[fn.fullscreenElement];
			}
		},
		enabled: {
			enumerable: true,
			get: function () {
				// Coerce to boolean in case of old WebKit
				return Boolean(document[fn.fullscreenEnabled]);
			}
		}
	});

	if (isCommonjs) {
		module.exports = screenfull;
	} else {
		window.screenfull = screenfull;
	}
})();
<commit_msg>Fix silly mistake in #104<commit_after>(function () {
	'use strict';

	var document = typeof window !== 'undefined' && typeof window.document !== 'undefined' ? window.document : {};
	var isCommonjs = typeof module !== 'undefined' && module.exports;
	var keyboardAllowed = typeof Element !== 'undefined' && 'ALLOW_KEYBOARD_INPUT' in Element;

	var fn = (function () {
		var val;

		var fnMap = [
			[
				'requestFullscreen',
				'exitFullscreen',
				'fullscreenElement',
				'fullscreenEnabled',
				'fullscreenchange',
				'fullscreenerror'
			],
			// New WebKit
			[
				'webkitRequestFullscreen',
				'webkitExitFullscreen',
				'webkitFullscreenElement',
				'webkitFullscreenEnabled',
				'webkitfullscreenchange',
				'webkitfullscreenerror'

			],
			// Old WebKit (Safari 5.1)
			[
				'webkitRequestFullScreen',
				'webkitCancelFullScreen',
				'webkitCurrentFullScreenElement',
				'webkitCancelFullScreen',
				'webkitfullscreenchange',
				'webkitfullscreenerror'

			],
			[
				'mozRequestFullScreen',
				'mozCancelFullScreen',
				'mozFullScreenElement',
				'mozFullScreenEnabled',
				'mozfullscreenchange',
				'mozfullscreenerror'
			],
			[
				'msRequestFullscreen',
				'msExitFullscreen',
				'msFullscreenElement',
				'msFullscreenEnabled',
				'MSFullscreenChange',
				'MSFullscreenError'
			]
		];

		var i = 0;
		var l = fnMap.length;
		var ret = {};

		for (; i < l; i++) {
			val = fnMap[i];
			if (val && val[1] in document) {
				for (i = 0; i < val.length; i++) {
					ret[fnMap[0][i]] = val[i];
				}
				return ret;
			}
		}

		return false;
	})();

	var eventNameMap = {
		change: fn.fullscreenchange,
		error: fn.fullscreenerror
	};

	var screenfull = {
		request: function (elem) {
			var request = fn.requestFullscreen;

			elem = elem || document.documentElement;

			// Work around Safari 5.1 bug: reports support for
			// keyboard in fullscreen even though it doesn't.
			// Browser sniffing, since the alternative with
			// setTimeout is even worse.
			if (/5\.1[.\d]* Safari/.test(navigator.userAgent)) {
				elem[request]();
			} else {
				elem[request](keyboardAllowed && Element.ALLOW_KEYBOARD_INPUT);
			}
		},
		exit: function () {
			document[fn.exitFullscreen]();
		},
		toggle: function (elem) {
			if (this.isFullscreen) {
				this.exit();
			} else {
				this.request(elem);
			}
		},
		onchange: function (callback) {
			this.on('change', callback);
		},
		onerror: function (callback) {
			this.on('error', callback);
		},
		on: function (event, callback) {
			var eventName = eventNameMap[event];
			if (eventName) {
				document.addEventListener(eventName, callback, false);
			}
		},
		off: function (event, callback) {
			var eventName = eventNameMap[event];
			if (eventName) {
				document.removeEventListener(eventName, callback, false);
			}
		},
		raw: fn
	};

	if (!fn) {
		if (isCommonjs) {
			module.exports = false;
		} else {
			window.screenfull = false;
		}

		return;
	}

	Object.defineProperties(screenfull, {
		isFullscreen: {
			get: function () {
				return Boolean(document[fn.fullscreenElement]);
			}
		},
		element: {
			enumerable: true,
			get: function () {
				return document[fn.fullscreenElement];
			}
		},
		enabled: {
			enumerable: true,
			get: function () {
				// Coerce to boolean in case of old WebKit
				return Boolean(document[fn.fullscreenEnabled]);
			}
		}
	});

	if (isCommonjs) {
		module.exports = screenfull;
	} else {
		window.screenfull = screenfull;
	}
})();
<|endoftext|>


<commit_before>import React from 'react'
import {connect} from 'react-redux'
import {Link, NavLink} from 'react-router-dom'
import {push} from 'react-router-redux'
import ReactMarkdown from 'react-markdown'
import Moment from 'moment'

import {getEvents} from '../actions.js'
import {
    showEditLink,
    showCreateLink,
    showBookLink,
    showManageLink,
    showApplyToBookLink,
    showBookingEditLink
} from '../permission.js'
import {getUserBookings} from '../../bookings/actions.js' //deep import, bad cause circular..
import {applyToBookEvent, manageEvent} from '../../../shared/permissions.js'

import {
    Button,
    Row,
    Col,
    Card,
    CardBody,
    CardTitle,
    CardSubtitle,
    CardImg
} from 'reactstrap';

import feeFactory           from '../../../shared/fee/feeFactory.js'

//Event listing

class EventList extends React.Component {
    constructor(props) {
        super(props);

        this.clickCreate = this.clickCreate.bind(this);
    }

    clickCreate(e) {
        e.preventDefault();
        dispatch(push('/event/create'));
    }

    render() {
        const user = this.props.User.toJS();
        let events = this.props.Events.toSet()
            .filter(e => new Date(e.get("startDate")) > new Date())
            .sort((a, b) => a.get("startDate").localeCompare(b.get("startDate")))
            .map((e) =>
                <Event User={user} {...e.toJS()} key={e.get("id")}/>)
            .toArray();

        let previousEvents = this.props.Events.toSet().filter(e => manageEvent(user, e.toJS()) && new Date(e.get("startDate")) < new Date())
            .sort((a, b) => a.get("startDate").localeCompare(b.get("startDate")) * -1)
            .map((e) =>
                <Event User={user} {...e.toJS()} key={e.get("id")}/>)
            .toArray();

        return (<React.Fragment>
            <Row>
                <Col>
                    {events}
                </Col>
            </Row>
            { previousEvents.length > 0 ? <React.Fragment>
            <Row>
                <Col>

Previous Events

                </Col>
            </Row>
            <Row>
                <Col>
                    {previousEvents}
                </Col>
            </Row>
            </React.Fragment> : null }
            <Row>
                <Col>
                    <CreateButton clickCreate={this.clickCreate}/>
                </Col>
            </Row>
        </React.Fragment>);

    }
}

const CreateButton = showCreateLink((props) => <Button color="success" onClick={props.clickCreate}>New
    Event</Button>);

const Event = (props) => {

    const EditLink = showEditLink(() => <NavLink event={props} to={"/event/" + props.id + "/edit"}>Edit</NavLink>);
    const ManageLink = showManageLink(() => <NavLink to={"/event/" + props.id + "/manage"}>Manage</NavLink>);
    const EditApplyButton = getEditApplyButton(props.User, props);

    return (<Card className="mb-3">
        <CardImg top src="/event-header.jpg" alt="Card image cap"/>
        <CardBody>
            <EditApplyButton event={props} booking={props.booking}/>
            <CardTitle>{props.name}</CardTitle>
            <CardSubtitle>{Moment(props.startDate).format('Do')} - {Moment(props.endDate).format('Do MMMM YYYY')}</CardSubtitle>

            {props.booking ? <MyBookingDisplay event={props} booking={props.booking} /> : null }
            <ReactMarkdown escapeHtml={true} source={props.description}/>

                <EditLink event={props}/>
                {" "}
                <ManageLink event={props}/>

        </CardBody>
    </Card>);
};

const MyBookingDisplay = props => {

    const fees = feeFactory(props.event);
    return <React.Fragment>

My booking

You have booked {props.booking.participants.length} participant{props.booking.participants.length > 1 ? 's' :''} into this event.

        <fees.ThanksRow
            event={props.event}
            booking={props.booking}/>
    </React.Fragment>
};

const getEditApplyButton = (user, event) => {
    if (event.bookingPolicy !== 'guest' && user.id === 1)
        return () => <Link to={"/user"}
                           className="btn btn-primary float-right">Log in to Book</Link>;

    if (event.booking !== undefined) {
        return showBookingEditLink(() => <Link to={"/booking/" + event.booking.id + "/edit"}
                                               className="btn btn-primary float-right">Edit
            My Booking</Link>)
    }
    if (applyToBookEvent(user, event)) {
        return showApplyToBookLink(() => <Link to={"/event/" + event.id + "/apply"}
                                               className="btn btn-primary float-right">Apply to book</Link>);
    }
    if (event.application !== undefined) {
        return () => <button className="btn btn-primary float-right disabled" disabled>Applied</button>;
    }
    return showBookLink(() => <Link to={"/event/" + event.id + "/book"}
                                    className="btn btn-primary float-right">Book</Link>)
};

//Connect to redux

const mapStateToProps = (state) => {

    const User = state.getIn(["User", "user"]);
    const userId = User.get("id");
    let Events = state.getIn(["Events", "events"]);
    const Bookings = state.getIn(["Bookings", "bookings"]);
    Events = Events.map(e => {
        e = e.set("booking", Bookings.find(b => b.get("eventId") === e.get('id') && b.get("userId") === userId));
        return e.set("application", User.get("applications").find(a => a.get('eventId') === e.get('id')));
    });
    return {User, Events};
}

const mapDispatchToProps = {
    getEvents: getEvents,
    getUserBookings: getUserBookings
};

var VisibleEventList = connect(
    mapStateToProps,
    mapDispatchToProps
)(EventList);

export default VisibleEventList;<commit_msg>Add payment instructions here<commit_after>import React from 'react'
import {connect} from 'react-redux'
import {Link, NavLink} from 'react-router-dom'
import {push} from 'react-router-redux'
import ReactMarkdown from 'react-markdown'
import Moment from 'moment'

import {getEvents} from '../actions.js'
import {
    showEditLink,
    showCreateLink,
    showBookLink,
    showManageLink,
    showApplyToBookLink,
    showBookingEditLink
} from '../permission.js'
import {getUserBookings} from '../../bookings/actions.js' //deep import, bad cause circular..
import {applyToBookEvent, manageEvent} from '../../../shared/permissions.js'

import {
    Button,
    Row,
    Col,
    Card,
    CardBody,
    CardTitle,
    CardSubtitle,
    CardImg
} from 'reactstrap';

import feeFactory           from '../../../shared/fee/feeFactory.js'

//Event listing

class EventList extends React.Component {
    constructor(props) {
        super(props);

        this.clickCreate = this.clickCreate.bind(this);
    }

    clickCreate(e) {
        e.preventDefault();
        dispatch(push('/event/create'));
    }

    render() {
        const user = this.props.User.toJS();
        let events = this.props.Events.toSet()
            .filter(e => new Date(e.get("startDate")) > new Date())
            .sort((a, b) => a.get("startDate").localeCompare(b.get("startDate")))
            .map((e) =>
                <Event User={user} {...e.toJS()} key={e.get("id")}/>)
            .toArray();

        let previousEvents = this.props.Events.toSet().filter(e => manageEvent(user, e.toJS()) && new Date(e.get("startDate")) < new Date())
            .sort((a, b) => a.get("startDate").localeCompare(b.get("startDate")) * -1)
            .map((e) =>
                <Event User={user} {...e.toJS()} key={e.get("id")}/>)
            .toArray();

        return (<React.Fragment>
            <Row>
                <Col>
                    {events}
                </Col>
            </Row>
            { previousEvents.length > 0 ? <React.Fragment>
            <Row>
                <Col>

Previous Events

                </Col>
            </Row>
            <Row>
                <Col>
                    {previousEvents}
                </Col>
            </Row>
            </React.Fragment> : null }
            <Row>
                <Col>
                    <CreateButton clickCreate={this.clickCreate}/>
                </Col>
            </Row>
        </React.Fragment>);

    }
}

const CreateButton = showCreateLink((props) => <Button color="success" onClick={props.clickCreate}>New
    Event</Button>);

const Event = (props) => {

    const EditLink = showEditLink(() => <NavLink event={props} to={"/event/" + props.id + "/edit"}>Edit</NavLink>);
    const ManageLink = showManageLink(() => <NavLink to={"/event/" + props.id + "/manage"}>Manage</NavLink>);
    const EditApplyButton = getEditApplyButton(props.User, props);

    return (<Card className="mb-3">
        <CardImg top src="/event-header.jpg" alt="Card image cap"/>
        <CardBody>
            <EditApplyButton event={props} booking={props.booking}/>
            <CardTitle>{props.name}</CardTitle>
            <CardSubtitle>{Moment(props.startDate).format('Do')} - {Moment(props.endDate).format('Do MMMM YYYY')}</CardSubtitle>

            {props.booking ? <MyBookingDisplay event={props} booking={props.booking} /> : null }
            <ReactMarkdown escapeHtml={true} source={props.description}/>

                <EditLink event={props}/>
                {" "}
                <ManageLink event={props}/>

        </CardBody>
    </Card>);
};

const MyBookingDisplay = props => {

    const fees = feeFactory(props.event);
    return <React.Fragment>

My booking

You have booked {props.booking.participants.length} participant{props.booking.participants.length > 1 ? 's' :''} into this event.

        <ReactMarkdown escapeHtml={true} source={props.event.feeData.desc}/>
        <fees.ThanksRow
            event={props.event}
            booking={props.booking}/>
    </React.Fragment>
};

const getEditApplyButton = (user, event) => {
    if (event.bookingPolicy !== 'guest' && user.id === 1)
        return () => <Link to={"/user"}
                           className="btn btn-primary float-right">Log in to Book</Link>;

    if (event.booking !== undefined) {
        return showBookingEditLink(() => <Link to={"/booking/" + event.booking.id + "/edit"}
                                               className="btn btn-primary float-right">Edit
            My Booking</Link>)
    }
    if (applyToBookEvent(user, event)) {
        return showApplyToBookLink(() => <Link to={"/event/" + event.id + "/apply"}
                                               className="btn btn-primary float-right">Apply to book</Link>);
    }
    if (event.application !== undefined) {
        return () => <button className="btn btn-primary float-right disabled" disabled>Applied</button>;
    }
    return showBookLink(() => <Link to={"/event/" + event.id + "/book"}
                                    className="btn btn-primary float-right">Book</Link>)
};

//Connect to redux

const mapStateToProps = (state) => {

    const User = state.getIn(["User", "user"]);
    const userId = User.get("id");
    let Events = state.getIn(["Events", "events"]);
    const Bookings = state.getIn(["Bookings", "bookings"]);
    Events = Events.map(e => {
        e = e.set("booking", Bookings.find(b => b.get("eventId") === e.get('id') && b.get("userId") === userId));
        return e.set("application", User.get("applications").find(a => a.get('eventId') === e.get('id')));
    });
    return {User, Events};
}

const mapDispatchToProps = {
    getEvents: getEvents,
    getUserBookings: getUserBookings
};

var VisibleEventList = connect(
    mapStateToProps,
    mapDispatchToProps
)(EventList);

export default VisibleEventList;<|endoftext|>