<commit_before>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide("frappe.ui.form");

frappe.ui.form.Attachments = Class.extend({
	init: function(opts) {
		$.extend(this, opts);
		this.make();
	},
	make: function() {
		var me = this;
		this.parent.find(".add-attachment").click(function() {
			me.new_attachment();
		});
		this.add_attachment_wrapper = this.parent.find(".add_attachment").parent();
		this.attachments_label = this.parent.find(".attachments-label");
	},
	max_reached: function() {
		// no of attachments
		var n = Object.keys(this.get_attachments()).length;

		// button if the number of attachments is less than max
		if(n < this.frm.meta.max_attachments || !this.frm.meta.max_attachments) {
			return false;
		}
		return true;
	},
	refresh: function() {
		var me = this;

		if(this.frm.doc.__islocal) {
			this.parent.toggle(false);
			return;
		}
		this.parent.toggle(true);
		this.parent.find(".attachment-row").remove();

		var max_reached = this.max_reached();
		this.add_attachment_wrapper.toggleClass("hide", !max_reached);

		// add attachment objects
		var attachments = this.get_attachments();
		if(attachments.length) {
			attachments.forEach(function(attachment) {
				me.add_attachment(attachment)
			});
		} else {
			this.attachments_label.removeClass("has-attachments");
		}

	},
	get_attachments: function() {
		return this.frm.get_docinfo().attachments;
	},
	add_attachment: function(attachment) {
		var file_name = attachment.file_name;
		var file_url = this.get_file_url(attachment);
		var fileid = attachment.name;
		if (!file_name) {
			file_name = file_url;
		}

		var me = this;
		var $attach = $(repl('
\
				×\
				%(lock_icon)s\
				\
					%(file_name)s\

', {
				lock_icon: attachment.is_private ? ' ': "",
				file_name: file_name,
				file_url: frappe.urllib.get_full_url(file_url)
			}))
			.insertAfter(this.attachments_label.addClass("has-attachments"));

		var $close =
			$attach.find(".close")
			.data("fileid", fileid)
			.click(function() {
				var remove_btn = this;
				frappe.confirm(__("Are you sure you want to delete the attachment?"),
					function() {
						me.remove_attachment($(remove_btn).data("fileid"))
					}
				);
				return false
			});

		if(!frappe.model.can_write(this.frm.doctype, this.frm.name)) {
			$close.remove();
		}
	},
	get_file_url: function(attachment) {
		var file_url = attachment.file_url;
		if (!file_url) {
			if (attachment.file_name.indexOf('files/') === 0) {
				file_url = '/' + attachment.file_name;
			}
			else {
				file_url = '/files/' + attachment.file_name;
			}
		}
		// hash is not escaped, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI
		return encodeURI(file_url).replace(/#/g, '%23');
	},
	get_file_id_from_file_url: function(file_url) {
		var fid;
		$.each(this.get_attachments(), function(i, attachment) {
			if (attachment.file_url === file_url) {
				fid = attachment.name;
				return false;
			}
		});
		return fid;
	},
	remove_attachment_by_filename: function(filename, callback) {
		this.remove_attachment(this.get_file_id_from_file_url(filename), callback);
	},
	remove_attachment: function(fileid, callback) {
		if (!fileid) {
			if (callback) callback();
			return;
		}

		var me = this;
		return frappe.call({
			method: 'frappe.desk.form.utils.remove_attach',
			args: {
				fid: fileid,
				dt: me.frm.doctype,
				dn: me.frm.docname
			},
			callback: function(r,rt) {
				if(r.exc) {
					if(!r._server_messages)
						frappe.msgprint(__("There were errors"));
					return;
				}
				me.remove_fileid(fileid);
				me.frm.get_docinfo().communications.push(r.message);
				me.frm.timeline.refresh();
				if (callback) callback();
			}
		});
	},
	new_attachment: function(fieldname) {
		var me = this;
		if (this.dialog) {
			// remove upload dialog
			this.dialog.$wrapper.remove();
		}

		// make upload dialog
		this.dialog = frappe.ui.get_upload_dialog({
			"args": me.get_args(),
			"callback": function(attachment, r) { me.attachment_uploaded(attachment, r) },
			"max_width": me.frm.cscript ? me.frm.cscript.attachment_max_width : null,
			"max_height": me.frm.cscript ? me.frm.cscript.attachment_max_height : null
		});
	},
	get_args: function() {
		return {
			from_form: 1,
			doctype: this.frm.doctype,
			docname: this.frm.docname,
		}
	},
	attachment_uploaded:  function(attachment, r) {
		this.dialog && this.dialog.hide();
		this.update_attachment(attachment, r.message.comment);

		if(this.fieldname) {
			this.frm.set_value(this.fieldname, attachment.file_url);
		}
	},
	update_attachment: function(attachment, comment) {
		if(attachment.name) {
			this.add_to_attachments(attachment);
			this.refresh();
			if(comment) {
				this.frm.get_docinfo().communications.push(comment);
				this.frm.timeline.refresh();
			}
		}
	},
	add_to_attachments: function (attachment) {
		var form_attachments = this.get_attachments();
		for(var i in form_attachments) {
			// prevent duplicate
			if(form_attachments[i]["name"] === attachment.name) return;
		}
		form_attachments.push(attachment);
	},
	remove_fileid: function(fileid) {
		var attachments = this.get_attachments();
		var new_attachments = [];
		$.each(attachments, function(i, attachment) {
			if(attachment.name!=fileid) {
				new_attachments.push(attachment);
			}
		});
		this.frm.get_docinfo().attachments = new_attachments;
		this.refresh();
	}
});

frappe.ui.get_upload_dialog = function(opts){
	var dialog = new frappe.ui.Dialog({
		title: __('Upload Attachment'),
		no_focus: true,
		fields: [
			{
				"fieldtype": "Section Break"
			},
			{
				"fieldtype": "Link" ,
				"fieldname": "file" ,
				"label": __("Select uploaded file"),
				"options": "File",
				onchange: function() {
					frappe.call({
						'method': 'frappe.client.get_value',
						'args': {
							'doctype': 'File',
							'fieldname': ['file_url','file_name','is_private'],
							'filters': {
								'name': dialog.get_value("file")
							}
						},
						callback: function(r){
							if(!r.message) {
								dialog.$wrapper.find('[name="file_url"]').val("");
								return;
							}
							dialog.$wrapper.find('[name="file_url"]').val(r.message.file_url);
							dialog.$wrapper.find('.private-file input').prop('checked', r.message.is_private);
							opts.args.filename = r.message.file_name;
							opts.args.is_private = r.message.is_private;
						}
					});
				}
			},
			{
				"hidden": !opts.args.doctype || !frappe.boot.gsuite_enabled,
				"fieldtype": "Section Break",
				"label": __("GSuite Document"),
			},
			{
				"fieldtype": "Link" ,
				"fieldname": "gs_template" ,
				"label": __("Select template"),
				"options": "GSuite Templates",
				"reqd" : false,
				"filters": {
					'related_doctype': opts.args.doctype
				},
				onchange: function(){
					opts.args.gs_template = this.get_value();
				}
			},
		],
	});
	var btn = dialog.set_primary_action(__("Attach"));
	btn.removeClass("btn-primary").addClass("btn-default");

	dialog.show();
	var upload_area = $('

').prependTo(dialog.body);

	frappe.upload.make({
		parent: upload_area,
		args: opts.args,
		callback: function(attachment, r) {
			dialog.hide();
			if(opts.callback){
				opts.callback(attachment, r);
			}
		},
		on_select: function() {
			btn.removeClass("btn-default").addClass("btn-primary");
		},
		onerror: function() {
			dialog.hide();
		},
		btn: btn,
		max_width: opts.max_width,
		max_height: opts.max_height,
	});

	return dialog;
}
<commit_msg>refresh only if timeline (#5050)<commit_after>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide("frappe.ui.form");

frappe.ui.form.Attachments = Class.extend({
	init: function(opts) {
		$.extend(this, opts);
		this.make();
	},
	make: function() {
		var me = this;
		this.parent.find(".add-attachment").click(function() {
			me.new_attachment();
		});
		this.add_attachment_wrapper = this.parent.find(".add_attachment").parent();
		this.attachments_label = this.parent.find(".attachments-label");
	},
	max_reached: function() {
		// no of attachments
		var n = Object.keys(this.get_attachments()).length;

		// button if the number of attachments is less than max
		if(n < this.frm.meta.max_attachments || !this.frm.meta.max_attachments) {
			return false;
		}
		return true;
	},
	refresh: function() {
		var me = this;

		if(this.frm.doc.__islocal) {
			this.parent.toggle(false);
			return;
		}
		this.parent.toggle(true);
		this.parent.find(".attachment-row").remove();

		var max_reached = this.max_reached();
		this.add_attachment_wrapper.toggleClass("hide", !max_reached);

		// add attachment objects
		var attachments = this.get_attachments();
		if(attachments.length) {
			attachments.forEach(function(attachment) {
				me.add_attachment(attachment)
			});
		} else {
			this.attachments_label.removeClass("has-attachments");
		}

	},
	get_attachments: function() {
		return this.frm.get_docinfo().attachments;
	},
	add_attachment: function(attachment) {
		var file_name = attachment.file_name;
		var file_url = this.get_file_url(attachment);
		var fileid = attachment.name;
		if (!file_name) {
			file_name = file_url;
		}

		var me = this;
		var $attach = $(repl('
\
				×\
				%(lock_icon)s\
				\
					%(file_name)s\

', {
				lock_icon: attachment.is_private ? ' ': "",
				file_name: file_name,
				file_url: frappe.urllib.get_full_url(file_url)
			}))
			.insertAfter(this.attachments_label.addClass("has-attachments"));

		var $close =
			$attach.find(".close")
			.data("fileid", fileid)
			.click(function() {
				var remove_btn = this;
				frappe.confirm(__("Are you sure you want to delete the attachment?"),
					function() {
						me.remove_attachment($(remove_btn).data("fileid"))
					}
				);
				return false
			});

		if(!frappe.model.can_write(this.frm.doctype, this.frm.name)) {
			$close.remove();
		}
	},
	get_file_url: function(attachment) {
		var file_url = attachment.file_url;
		if (!file_url) {
			if (attachment.file_name.indexOf('files/') === 0) {
				file_url = '/' + attachment.file_name;
			}
			else {
				file_url = '/files/' + attachment.file_name;
			}
		}
		// hash is not escaped, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI
		return encodeURI(file_url).replace(/#/g, '%23');
	},
	get_file_id_from_file_url: function(file_url) {
		var fid;
		$.each(this.get_attachments(), function(i, attachment) {
			if (attachment.file_url === file_url) {
				fid = attachment.name;
				return false;
			}
		});
		return fid;
	},
	remove_attachment_by_filename: function(filename, callback) {
		this.remove_attachment(this.get_file_id_from_file_url(filename), callback);
	},
	remove_attachment: function(fileid, callback) {
		if (!fileid) {
			if (callback) callback();
			return;
		}

		var me = this;
		return frappe.call({
			method: 'frappe.desk.form.utils.remove_attach',
			args: {
				fid: fileid,
				dt: me.frm.doctype,
				dn: me.frm.docname
			},
			callback: function(r,rt) {
				if(r.exc) {
					if(!r._server_messages)
						frappe.msgprint(__("There were errors"));
					return;
				}
				me.remove_fileid(fileid);
				me.frm.get_docinfo().communications.push(r.message);
				me.frm.timeline && me.frm.timeline.refresh();
				if (callback) callback();
			}
		});
	},
	new_attachment: function(fieldname) {
		var me = this;
		if (this.dialog) {
			// remove upload dialog
			this.dialog.$wrapper.remove();
		}

		// make upload dialog
		this.dialog = frappe.ui.get_upload_dialog({
			"args": me.get_args(),
			"callback": function(attachment, r) { me.attachment_uploaded(attachment, r) },
			"max_width": me.frm.cscript ? me.frm.cscript.attachment_max_width : null,
			"max_height": me.frm.cscript ? me.frm.cscript.attachment_max_height : null
		});
	},
	get_args: function() {
		return {
			from_form: 1,
			doctype: this.frm.doctype,
			docname: this.frm.docname,
		}
	},
	attachment_uploaded:  function(attachment, r) {
		this.dialog && this.dialog.hide();
		this.update_attachment(attachment, r.message.comment);

		if(this.fieldname) {
			this.frm.set_value(this.fieldname, attachment.file_url);
		}
	},
	update_attachment: function(attachment, comment) {
		if(attachment.name) {
			this.add_to_attachments(attachment);
			this.refresh();
			if(comment) {
				this.frm.get_docinfo().communications.push(comment);
				this.frm.timeline.refresh();
			}
		}
	},
	add_to_attachments: function (attachment) {
		var form_attachments = this.get_attachments();
		for(var i in form_attachments) {
			// prevent duplicate
			if(form_attachments[i]["name"] === attachment.name) return;
		}
		form_attachments.push(attachment);
	},
	remove_fileid: function(fileid) {
		var attachments = this.get_attachments();
		var new_attachments = [];
		$.each(attachments, function(i, attachment) {
			if(attachment.name!=fileid) {
				new_attachments.push(attachment);
			}
		});
		this.frm.get_docinfo().attachments = new_attachments;
		this.refresh();
	}
});

frappe.ui.get_upload_dialog = function(opts){
	var dialog = new frappe.ui.Dialog({
		title: __('Upload Attachment'),
		no_focus: true,
		fields: [
			{
				"fieldtype": "Section Break"
			},
			{
				"fieldtype": "Link" ,
				"fieldname": "file" ,
				"label": __("Select uploaded file"),
				"options": "File",
				onchange: function() {
					frappe.call({
						'method': 'frappe.client.get_value',
						'args': {
							'doctype': 'File',
							'fieldname': ['file_url','file_name','is_private'],
							'filters': {
								'name': dialog.get_value("file")
							}
						},
						callback: function(r){
							if(!r.message) {
								dialog.$wrapper.find('[name="file_url"]').val("");
								return;
							}
							dialog.$wrapper.find('[name="file_url"]').val(r.message.file_url);
							dialog.$wrapper.find('.private-file input').prop('checked', r.message.is_private);
							opts.args.filename = r.message.file_name;
							opts.args.is_private = r.message.is_private;
						}
					});
				}
			},
			{
				"hidden": !opts.args.doctype || !frappe.boot.gsuite_enabled,
				"fieldtype": "Section Break",
				"label": __("GSuite Document"),
			},
			{
				"fieldtype": "Link" ,
				"fieldname": "gs_template" ,
				"label": __("Select template"),
				"options": "GSuite Templates",
				"reqd" : false,
				"filters": {
					'related_doctype': opts.args.doctype
				},
				onchange: function(){
					opts.args.gs_template = this.get_value();
				}
			},
		],
	});
	var btn = dialog.set_primary_action(__("Attach"));
	btn.removeClass("btn-primary").addClass("btn-default");

	dialog.show();
	var upload_area = $('

').prependTo(dialog.body);

	frappe.upload.make({
		parent: upload_area,
		args: opts.args,
		callback: function(attachment, r) {
			dialog.hide();
			if(opts.callback){
				opts.callback(attachment, r);
			}
		},
		on_select: function() {
			btn.removeClass("btn-default").addClass("btn-primary");
		},
		onerror: function() {
			dialog.hide();
		},
		btn: btn,
		max_width: opts.max_width,
		max_height: opts.max_height,
	});

	return dialog;
}
<|endoftext|>


<commit_before>/**
\file mobile.js
Plugin to capture navigator.connection.type on browsers that support it
*/

BOOMR.addVar("mob.ct", (typeof navigator === 'object' && navigator.connection) ? navigator.connection.type : 0);
<commit_msg>Add bandwidth and metered as well from connection<commit_after>/**
\file mobile.js
Plugin to capture navigator.connection.type on browsers that support it
*/

(function() {
var connection;

	if(typeof navigator === 'object') {
		connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection || navigator.msConnection;
	}

	if(!connection) {
		return;
	}
	BOOMR.addVar("mob.ct", connection.type);
	BOOMR.addVar("mob.bw", connection.bandwidth);
	BOOMR.addVar("mob.mt", connection.metered);

}());
<|endoftext|>


<commit_before>/* globals hqDefine */
hqDefine("hqwebapp/js/main", function() {
    var eventize = function (that) {
        'use strict';
        var events = {};
        that.on = function (tag, callback) {
            if (events[tag] === undefined) {
                events[tag] = [];
            }
            events[tag].push(callback);
            return that;
        };
        that.fire = function (tag, e) {
            var i;
            if (events[tag] !== undefined) {
                for (i = 0; i < events[tag].length; i += 1) {
                    events[tag][i].apply(that, [e]);
                }
            }
            return that;
        };
        return that;
    };

    var makeHqHelp = function (opts, wrap) {
        'use strict';
        wrap = wrap === undefined ? true : wrap;
        var el = $(
            '
' +
                '' +
                    '
'
        );
        _.each(['content', 'title', 'html', 'placement', 'container'], function(attr) {
            $('a', el).data(attr, opts[attr]);
        });
        if (wrap) {
            el.hqHelp();
        }
        return el;
    };

    var transformHelpTemplate = function ($template, wrap) {
        'use strict';
        if ($template.data()) {
            var $help = makeHqHelp($template.data(), wrap);
            $help.insertAfter($template);
            $template.remove();
        }
    };

    var initBlock = function ($elem) {
        'use strict';

        $('.submit').click(function (e) {
            var $form = $(this).closest('.form, form'),
                data = $form.find('[name]').serialize(),
                action = $form.attr('action') || $form.data('action');

            e.preventDefault();
            $.postGo(action, $.unparam(data));
        });
        $('.post-link').click(function (e) {
            e.preventDefault();
            $.postGo($(this).attr('href'), {});
        });

        $(".button", $elem).button().wrap('');
        $("input[type='submit']", $elem).button();
        $("input[type='text'], input[type='password'], textarea", $elem);
        $('.container', $elem).addClass('ui-widget ui-widget-content');
        $('.config', $elem).wrap('
').parent().addClass('container block ui-corner-all');

        $('.hq-help-template').each(function () {
            transformHelpTemplate($(this), true);
        });
    };

    var updateDOM = function (update) {
        'use strict';
        var key;
        for (key in update) {
            if (update.hasOwnProperty(key)) {
                $(key).text(update[key]).val(update[key]);
            }
        }
    };

    var makeSaveButton = function(messageStrings, cssClass, barClass) {
        'use strict';
        var BAR_STATE = {
            SAVE: 'savebtn-bar-save',
            SAVING: 'savebtn-bar-saving',
            SAVED: 'savebtn-bar-saved',
            RETRY: 'savebtn-bar-retry',
        };
        barClass = barClass || '';
        var SaveButton = {
            /*
             options: {
             save: "Function to call when the user clicks Save",
             unsavedMessage: "Message to display when there are unsaved changes and the user leaves the page"
             }
             */
            init: function (options) {
                var button = {
                    $save: $('
').text(SaveButton.message.SAVE).click(function () {
                        button.fire('save');
                    }).addClass(cssClass),
                    $retry: $('
').text(SaveButton.message.RETRY).click(function () {
                        button.fire('save');
                    }).addClass(cssClass),
                    $saving: $('
').text(SaveButton.message.SAVING).addClass('btn btn-default disabled'),
                    $saved: $('
').text(SaveButton.message.SAVED).addClass('btn btn-default disabled'),
                    ui: $('
').addClass('pull-right savebtn-bar ' + barClass),
                    setStateWhenReady: function (state) {
                        if (this.state === 'saving') {
                            this.nextState = state;
                        } else {
                            this.setState(state);
                        }
                    },
                    setState: function (state) {
                        if (this.state === state) {
                            return;
                        }
                        this.state = state;
                        this.$save.detach();
                        this.$saving.detach();
                        this.$saved.detach();
                        this.$retry.detach();
                        var buttonUi = this.ui;
                        _.each(BAR_STATE, function (v, k) {
                            buttonUi.removeClass(v);
                        });
                        if (state === 'save') {
                            this.ui.addClass(BAR_STATE.SAVE);
                            this.ui.append(this.$save);
                        } else if (state === 'saving') {
                            this.ui.addClass(BAR_STATE.SAVING);
                            this.ui.append(this.$saving);
                        } else if (state === 'saved') {
                            this.ui.addClass(BAR_STATE.SAVED);
                            this.ui.append(this.$saved);
                        } else if (state === 'retry') {
                            this.ui.addClass(BAR_STATE.RETRY);
                            this.ui.append(this.$retry);
                        }
                    },
                    ajax: function (options) {
                        var beforeSend = options.beforeSend || function () {},
                            success = options.success || function () {},
                            error = options.error || function () {},
                            that = this;
                        options.beforeSend = function (jqXHR, settings) {
                            that.setState('saving');
                            that.nextState = 'saved';
                            $.ajaxSettings.beforeSend(jqXHR, settings);
                            beforeSend.apply(this, arguments);
                        };
                        options.success = function (data) {
                            that.setState(that.nextState);
                            success.apply(this, arguments);
                        };
                        options.error = function (data) {
                            that.nextState = null;
                            that.setState('retry');
                            var customError = ((data.responseJSON && data.responseJSON.message) ? data.responseJSON.message : data.responseText);
                            if (customError.indexOf('') > -1) {
                                // this is sending back a full html page, likely login, so no error message.
                                customError = null;
                            }
                            hqImport("hqwebapp/js/alert_user").alert_user(customError || SaveButton.message.ERROR_SAVING, 'danger');
                            error.apply(this, arguments);
                        };
                        var jqXHR = $.ajax(options);
                        if (!jqXHR) {
                            // request was aborted
                            that.setState('save');
                        }
                    }
                };
                eventize(button);
                button.setState('saved');
                button.on('change', function () {
                    this.setStateWhenReady('save');
                });
                if (options.save) {
                    button.on('save', options.save);
                }
                $(window).on('beforeunload', function () {
                    var lastParent = button.ui.parents()[button.ui.parents().length - 1];
                    if (lastParent) {
                        var stillAttached = lastParent.tagName.toLowerCase() == 'html';
                        if (button.state !== 'saved' && stillAttached) {
                            if ($('.js-unhide-on-unsaved').length > 0) $('.js-unhide-on-unsaved').removeClass('hide');
                            return options.unsavedMessage || "";
                        }
                    }
                });
                return button;
            },
            initForm: function ($form, options) {
                var url = $form.attr('action'),
                    button = SaveButton.init({
                        unsavedMessage: options.unsavedMessage,
                        save: function () {
                            this.ajax({
                                url: url,
                                type: 'POST',
                                dataType: 'json',
                                data: $form.serialize(),
                                success: options.success
                            });
                        }
                    }),
                    fireChange = function () {
                        button.fire('change');
                    };
                _.defer(function () {
                    $form.find('*').change(fireChange);
                    $form.find('input, textarea').on('textchange', fireChange);
                });
                return button;
            },
            message: messageStrings
        };

        return SaveButton;
    };

    var SaveButton = makeSaveButton({
        SAVE: django.gettext("Save"),
        SAVING: django.gettext("Saving..."),
        SAVED: django.gettext("Saved"),
        RETRY: django.gettext("Try Again"),
        ERROR_SAVING: django.gettext("There was an error saving")
    }, 'btn btn-success');

    var DeleteButton = makeSaveButton({
        SAVE: django.gettext("Delete"),
        SAVING: django.gettext("Deleting..."),
        SAVED: django.gettext("Deleted"),
        RETRY: django.gettext("Try Again"),
        ERROR_SAVING: django.gettext("There was an error deleting")
    }, 'btn btn-danger', 'savebtn-bar-danger');

    var beforeUnload = [];
    var bindBeforeUnload = function (callback) {
        beforeUnload.push(callback);
    };
    var beforeUnloadCallback = function () {
        for (var i = 0; i < beforeUnload.length; i++) {
            var message = beforeUnload[i]();
            if (message !== null && message !== undefined) {
                return message;
            }
        }
    };

    $(function () {
        'use strict';
        $(window).on('beforeunload', beforeUnloadCallback);
        initBlock($("body"));

        $('#modalTrial30Day').modal('show');

        $(document).on('click', '.track-usage-link', function(e) {
            var $link = $(e.currentTarget),
                data = $link.data();
            hqImport('analytics/js/google').track.click($link, data.category, data.action, data.label, data.value);
        });

        $(document).on('click', '.mainmenu-tab a', function(e) {
            if (typeof(ga) !== 'undefined') {
                var data = $(e.currentTarget).closest(".mainmenu-tab").data();
                if (data.category && data.action) {
                    ga('send', 'event', data.category, data.action, data.label);
                }
            }
        });

        // EULA and CDA modals
        _.each($(".remote-modal"), function(modal) {
            var $modal = $(modal);
            $modal.on("show show.bs.modal", function() {
                $(this).find(".fetched-data").load($(this).data("url"));
            });
            if ($modal.data("showOnPageLoad")) {
                $modal.modal('show');
            }
        });
    });

    var capitalize = function(string) {
        return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
    };

    return {
        beforeUnloadCallback: beforeUnloadCallback,
        eventize: eventize,
        icons: {
            GRIP: 'icon-resize-vertical icon-blue fa fa-arrows-v',
            ADD: 'icon-plus icon-blue fa fa-plus',
            DELETE: 'icon-remove icon-blue fa fa-remove',
        },
        initBlock: initBlock,
        initDeleteButton: DeleteButton.init,
        initSaveButton: SaveButton.init,
        makeSaveButton: makeSaveButton,
        SaveButton: SaveButton,
        initSaveButtonForm: SaveButton.initForm,
        makeHqHelp: makeHqHelp,
        transformHelpTemplate: transformHelpTemplate,
        updateDOM: updateDOM,
        capitalize: capitalize,
    };
});
<commit_msg>retire ga()<commit_after>/* globals hqDefine */
hqDefine("hqwebapp/js/main", function() {
    var eventize = function (that) {
        'use strict';
        var events = {};
        that.on = function (tag, callback) {
            if (events[tag] === undefined) {
                events[tag] = [];
            }
            events[tag].push(callback);
            return that;
        };
        that.fire = function (tag, e) {
            var i;
            if (events[tag] !== undefined) {
                for (i = 0; i < events[tag].length; i += 1) {
                    events[tag][i].apply(that, [e]);
                }
            }
            return that;
        };
        return that;
    };

    var makeHqHelp = function (opts, wrap) {
        'use strict';
        wrap = wrap === undefined ? true : wrap;
        var el = $(
            '
' +
                '' +
                    '
'
        );
        _.each(['content', 'title', 'html', 'placement', 'container'], function(attr) {
            $('a', el).data(attr, opts[attr]);
        });
        if (wrap) {
            el.hqHelp();
        }
        return el;
    };

    var transformHelpTemplate = function ($template, wrap) {
        'use strict';
        if ($template.data()) {
            var $help = makeHqHelp($template.data(), wrap);
            $help.insertAfter($template);
            $template.remove();
        }
    };

    var initBlock = function ($elem) {
        'use strict';

        $('.submit').click(function (e) {
            var $form = $(this).closest('.form, form'),
                data = $form.find('[name]').serialize(),
                action = $form.attr('action') || $form.data('action');

            e.preventDefault();
            $.postGo(action, $.unparam(data));
        });
        $('.post-link').click(function (e) {
            e.preventDefault();
            $.postGo($(this).attr('href'), {});
        });

        $(".button", $elem).button().wrap('');
        $("input[type='submit']", $elem).button();
        $("input[type='text'], input[type='password'], textarea", $elem);
        $('.container', $elem).addClass('ui-widget ui-widget-content');
        $('.config', $elem).wrap('
').parent().addClass('container block ui-corner-all');

        $('.hq-help-template').each(function () {
            transformHelpTemplate($(this), true);
        });
    };

    var updateDOM = function (update) {
        'use strict';
        var key;
        for (key in update) {
            if (update.hasOwnProperty(key)) {
                $(key).text(update[key]).val(update[key]);
            }
        }
    };

    var makeSaveButton = function(messageStrings, cssClass, barClass) {
        'use strict';
        var BAR_STATE = {
            SAVE: 'savebtn-bar-save',
            SAVING: 'savebtn-bar-saving',
            SAVED: 'savebtn-bar-saved',
            RETRY: 'savebtn-bar-retry',
        };
        barClass = barClass || '';
        var SaveButton = {
            /*
             options: {
             save: "Function to call when the user clicks Save",
             unsavedMessage: "Message to display when there are unsaved changes and the user leaves the page"
             }
             */
            init: function (options) {
                var button = {
                    $save: $('
').text(SaveButton.message.SAVE).click(function () {
                        button.fire('save');
                    }).addClass(cssClass),
                    $retry: $('
').text(SaveButton.message.RETRY).click(function () {
                        button.fire('save');
                    }).addClass(cssClass),
                    $saving: $('
').text(SaveButton.message.SAVING).addClass('btn btn-default disabled'),
                    $saved: $('
').text(SaveButton.message.SAVED).addClass('btn btn-default disabled'),
                    ui: $('
').addClass('pull-right savebtn-bar ' + barClass),
                    setStateWhenReady: function (state) {
                        if (this.state === 'saving') {
                            this.nextState = state;
                        } else {
                            this.setState(state);
                        }
                    },
                    setState: function (state) {
                        if (this.state === state) {
                            return;
                        }
                        this.state = state;
                        this.$save.detach();
                        this.$saving.detach();
                        this.$saved.detach();
                        this.$retry.detach();
                        var buttonUi = this.ui;
                        _.each(BAR_STATE, function (v, k) {
                            buttonUi.removeClass(v);
                        });
                        if (state === 'save') {
                            this.ui.addClass(BAR_STATE.SAVE);
                            this.ui.append(this.$save);
                        } else if (state === 'saving') {
                            this.ui.addClass(BAR_STATE.SAVING);
                            this.ui.append(this.$saving);
                        } else if (state === 'saved') {
                            this.ui.addClass(BAR_STATE.SAVED);
                            this.ui.append(this.$saved);
                        } else if (state === 'retry') {
                            this.ui.addClass(BAR_STATE.RETRY);
                            this.ui.append(this.$retry);
                        }
                    },
                    ajax: function (options) {
                        var beforeSend = options.beforeSend || function () {},
                            success = options.success || function () {},
                            error = options.error || function () {},
                            that = this;
                        options.beforeSend = function (jqXHR, settings) {
                            that.setState('saving');
                            that.nextState = 'saved';
                            $.ajaxSettings.beforeSend(jqXHR, settings);
                            beforeSend.apply(this, arguments);
                        };
                        options.success = function (data) {
                            that.setState(that.nextState);
                            success.apply(this, arguments);
                        };
                        options.error = function (data) {
                            that.nextState = null;
                            that.setState('retry');
                            var customError = ((data.responseJSON && data.responseJSON.message) ? data.responseJSON.message : data.responseText);
                            if (customError.indexOf('') > -1) {
                                // this is sending back a full html page, likely login, so no error message.
                                customError = null;
                            }
                            hqImport("hqwebapp/js/alert_user").alert_user(customError || SaveButton.message.ERROR_SAVING, 'danger');
                            error.apply(this, arguments);
                        };
                        var jqXHR = $.ajax(options);
                        if (!jqXHR) {
                            // request was aborted
                            that.setState('save');
                        }
                    }
                };
                eventize(button);
                button.setState('saved');
                button.on('change', function () {
                    this.setStateWhenReady('save');
                });
                if (options.save) {
                    button.on('save', options.save);
                }
                $(window).on('beforeunload', function () {
                    var lastParent = button.ui.parents()[button.ui.parents().length - 1];
                    if (lastParent) {
                        var stillAttached = lastParent.tagName.toLowerCase() == 'html';
                        if (button.state !== 'saved' && stillAttached) {
                            if ($('.js-unhide-on-unsaved').length > 0) $('.js-unhide-on-unsaved').removeClass('hide');
                            return options.unsavedMessage || "";
                        }
                    }
                });
                return button;
            },
            initForm: function ($form, options) {
                var url = $form.attr('action'),
                    button = SaveButton.init({
                        unsavedMessage: options.unsavedMessage,
                        save: function () {
                            this.ajax({
                                url: url,
                                type: 'POST',
                                dataType: 'json',
                                data: $form.serialize(),
                                success: options.success
                            });
                        }
                    }),
                    fireChange = function () {
                        button.fire('change');
                    };
                _.defer(function () {
                    $form.find('*').change(fireChange);
                    $form.find('input, textarea').on('textchange', fireChange);
                });
                return button;
            },
            message: messageStrings
        };

        return SaveButton;
    };

    var SaveButton = makeSaveButton({
        SAVE: django.gettext("Save"),
        SAVING: django.gettext("Saving..."),
        SAVED: django.gettext("Saved"),
        RETRY: django.gettext("Try Again"),
        ERROR_SAVING: django.gettext("There was an error saving")
    }, 'btn btn-success');

    var DeleteButton = makeSaveButton({
        SAVE: django.gettext("Delete"),
        SAVING: django.gettext("Deleting..."),
        SAVED: django.gettext("Deleted"),
        RETRY: django.gettext("Try Again"),
        ERROR_SAVING: django.gettext("There was an error deleting")
    }, 'btn btn-danger', 'savebtn-bar-danger');

    var beforeUnload = [];
    var bindBeforeUnload = function (callback) {
        beforeUnload.push(callback);
    };
    var beforeUnloadCallback = function () {
        for (var i = 0; i < beforeUnload.length; i++) {
            var message = beforeUnload[i]();
            if (message !== null && message !== undefined) {
                return message;
            }
        }
    };

    $(function () {
        'use strict';
        $(window).on('beforeunload', beforeUnloadCallback);
        initBlock($("body"));

        $('#modalTrial30Day').modal('show');

        $(document).on('click', '.track-usage-link', function(e) {
            var $link = $(e.currentTarget),
                data = $link.data();
            hqImport('analytics/js/google').track.click($link, data.category, data.action, data.label, data.value);
        });

        $(document).on('click', '.mainmenu-tab a', function(e) {
            var data = $(e.currentTarget).closest(".mainmenu-tab").data();
            if (data.category && data.action) {
                hqImport('analytics/js/google').track.event(data.category, data.action, data.label);
            }
        });

        // EULA and CDA modals
        _.each($(".remote-modal"), function(modal) {
            var $modal = $(modal);
            $modal.on("show show.bs.modal", function() {
                $(this).find(".fetched-data").load($(this).data("url"));
            });
            if ($modal.data("showOnPageLoad")) {
                $modal.modal('show');
            }
        });
    });

    var capitalize = function(string) {
        return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
    };

    return {
        beforeUnloadCallback: beforeUnloadCallback,
        eventize: eventize,
        icons: {
            GRIP: 'icon-resize-vertical icon-blue fa fa-arrows-v',
            ADD: 'icon-plus icon-blue fa fa-plus',
            DELETE: 'icon-remove icon-blue fa fa-remove',
        },
        initBlock: initBlock,
        initDeleteButton: DeleteButton.init,
        initSaveButton: SaveButton.init,
        makeSaveButton: makeSaveButton,
        SaveButton: SaveButton,
        initSaveButtonForm: SaveButton.initForm,
        makeHqHelp: makeHqHelp,
        transformHelpTemplate: transformHelpTemplate,
        updateDOM: updateDOM,
        capitalize: capitalize,
    };
});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import withIntl from '../lib/withIntl';
import { defineMessages, FormattedMessage, FormattedNumber, FormattedDate } from 'react-intl';
import { capitalize, formatCurrency } from '../lib/utils';
import { graphql } from 'react-apollo'
import gql from 'graphql-tag'
import Avatar from './Avatar';
import ExpenseDetails from './ExpenseDetails';
import ApproveExpenseBtn from './ApproveExpenseBtn';
import RejectExpenseBtn from './RejectExpenseBtn';
import PayExpenseBtn from './PayExpenseBtn';
import { Link } from '../server/pages';
import SmallButton from './SmallButton';

class Expense extends React.Component {

  static propTypes = {
    collective: PropTypes.object,
    expense: PropTypes.object,
    editable: PropTypes.bool,
    includeHostedCollectives: PropTypes.bool,
    LoggedInUser: PropTypes.object,
    allowPayAction: PropTypes.bool,
    lockPayAction: PropTypes.func,
    unlockPayAction: PropTypes.func
  }

  constructor(props) {
    super(props);

    this.state = {
      modified: false,
      expense: {},
      mode: 'summary'
    };

    this.save = this.save.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.toggleDetails = this.toggleDetails.bind(this);
    this.toggleEdit = this.toggleEdit.bind(this);
    this.messages = defineMessages({
      'pending': { id: 'expense.pending', defaultMessage: 'pending' },
      'paid': { id: 'expense.paid', defaultMessage: 'paid' },
      'approved': { id: 'expense.approved', defaultMessage: 'approved' },
      'rejected': { id: 'expense.rejected', defaultMessage: 'rejected' },
      'closeDetails': { id: 'expense.closeDetails', defaultMessage: 'Close Details' },
      'edit': { id: 'expense.edit', defaultMessage: 'edit' },
      'cancelEdit': { id: 'expense.cancelEdit', defaultMessage: 'cancel edit' },
      'viewDetails': { id: 'expense.viewDetails', defaultMessage: 'View Details' }
    });
    this.currencyStyle = { style: 'currency', currencyDisplay: 'symbol', minimumFractionDigits: 2, maximumFractionDigits: 2};
  }

  componentWillReceiveProps(newProps) {
    const { LoggedInUser } = newProps;
    const { expense } = this.props;
    if (LoggedInUser && this.state.mode === 'summary') {
      let mode = 'summary';
      if (LoggedInUser) {
        if (expense.status === 'PENDING' && LoggedInUser.canApproveExpense(expense)) {
          mode = 'details';
        }
        if (expense.status === 'APPROVED' && LoggedInUser.canPayExpense(expense)) {
          mode = 'details';
        }
      }

      this.setState({ mode });
    }
  }

  toggleDetails() {
    this.setState({
      mode: this.state.mode === 'details' ? 'summary' : 'details',
    });
  }

  cancelEdit() {
    this.setState({ modified: false, mode: 'details' });
  }

  edit() {
    this.setState({ modified: false, mode: 'edit' });
  }

  toggleEdit() {
    this.state.mode === 'edit' ? this.cancelEdit() : this.edit();
  }

  handleChange(expense) {
    this.setState({ modified: true, expense });
  }

  async save() {
    const expense = {
      id: this.props.expense.id,
      ...this.state.expense
    }
    const res = await this.props.editExpense(expense);
    this.setState({ modified: false, mode: 'details' });
  }

  render() {
    const {
      intl,
      collective,
      expense,
      includeHostedCollectives,
      LoggedInUser,
      editable
    } = this.props;

    const title = expense.description;
    const status = expense.status.toLowerCase();

    return (
      <div className={`expense ${status} ${this.state.mode}View`}>

          <a href={`/${expense.fromCollective.slug}`} title={expense.fromCollective.name}>
            <Avatar src={expense.fromCollective.image} key={expense.fromCollective.id} radius={40} />

            {/* should link to `/${collective.slug}/expenses/${expense.uuid}` once we have a page for it */}
              {capitalize(title)}

            <FormattedDate value={expense.incurredAt} day="numeric" month="numeric" /> |
            { includeHostedCollectives &&
              <Link route={`/${expense.collective.slug}`}>{expense.collective.slug}</Link> (balance: {formatCurrency(expense.collective.stats.balance, expense.collective.currency)}) |
            }
            {intl.formatMessage(this.messages[status])} |
            {` ${capitalize(expense.category)}`}
            { editable && LoggedInUser && LoggedInUser.canEditExpense(expense) &&
               | {intl.formatMessage(this.messages[`${this.state.mode === 'edit' ? 'cancelEdit' : 'edit'}`])}
            }
            { this.state.mode !== 'edit' &&
               | {intl.formatMessage(this.messages[`${this.state.mode === 'details' ? 'closeDetails' : 'viewDetails'}`])}
            }

          <ExpenseDetails
            LoggedInUser={LoggedInUser}
            expense={expense}
            collective={collective}
            onChange={this.handleChange}
            mode={this.state.mode}
            />

            { this.state.mode === 'edit' && this.state.modified &&

                  <SmallButton className="primary" onClick={this.save}><FormattedMessage id="expense.save" defaultMessage="save" /></SmallButton>

            }
            { this.state.mode !== 'edit' && LoggedInUser && LoggedInUser.canApproveExpense(expense) &&

                { expense.status === 'APPROVED' && LoggedInUser.canPayExpense(expense) &&
                  <PayExpenseBtn
                    expense={expense}
                    disabled={!this.props.allowPayAction}
                    lock={this.props.lockPayAction}
                    unlock={this.props.unlockPayAction}
                    />
                }
                { expense.status !== 'APPROVED' && <ApproveExpenseBtn id={expense.id} /> }
                { expense.status !== 'REJECTED' && <RejectExpenseBtn id={expense.id} /> }

            }

          <FormattedNumber
            value={expense.amount / 100}
            currency={expense.currency}
            {...this.currencyStyle}
            />

    );
  }
}

const editExpenseQuery = gql`
mutation editExpense($expense: ExpenseInputType!) {
  editExpense(expense: $expense) {
    id
    description
    amount
    attachment
    category
    privateMessage
    payoutMethod
    status
  }
}
`;

const addMutation = graphql(editExpenseQuery, {
props: ( { mutate }) => ({
  editExpense: async (expense) => {
    return await mutate({ variables: { expense } })
  }
})
});

export default withIntl(addMutation(Expense));<commit_msg>fixes opencollective/opencollective#756<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import withIntl from '../lib/withIntl';
import { defineMessages, FormattedMessage, FormattedNumber, FormattedDate } from 'react-intl';
import { capitalize, formatCurrency } from '../lib/utils';
import { graphql } from 'react-apollo'
import gql from 'graphql-tag'
import Avatar from './Avatar';
import ExpenseDetails from './ExpenseDetails';
import ApproveExpenseBtn from './ApproveExpenseBtn';
import RejectExpenseBtn from './RejectExpenseBtn';
import PayExpenseBtn from './PayExpenseBtn';
import { Link } from '../server/pages';
import SmallButton from './SmallButton';

class Expense extends React.Component {

  static propTypes = {
    collective: PropTypes.object,
    expense: PropTypes.object,
    editable: PropTypes.bool,
    includeHostedCollectives: PropTypes.bool,
    LoggedInUser: PropTypes.object,
    allowPayAction: PropTypes.bool,
    lockPayAction: PropTypes.func,
    unlockPayAction: PropTypes.func
  }

  constructor(props) {
    super(props);

    this.state = {
      modified: false,
      expense: {},
      mode: 'summary'
    };

    this.save = this.save.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.toggleDetails = this.toggleDetails.bind(this);
    this.toggleEdit = this.toggleEdit.bind(this);
    this.messages = defineMessages({
      'pending': { id: 'expense.pending', defaultMessage: 'pending' },
      'paid': { id: 'expense.paid', defaultMessage: 'paid' },
      'approved': { id: 'expense.approved', defaultMessage: 'approved' },
      'rejected': { id: 'expense.rejected', defaultMessage: 'rejected' },
      'closeDetails': { id: 'expense.closeDetails', defaultMessage: 'Close Details' },
      'edit': { id: 'expense.edit', defaultMessage: 'edit' },
      'cancelEdit': { id: 'expense.cancelEdit', defaultMessage: 'cancel edit' },
      'viewDetails': { id: 'expense.viewDetails', defaultMessage: 'View Details' }
    });
    this.currencyStyle = { style: 'currency', currencyDisplay: 'symbol', minimumFractionDigits: 2, maximumFractionDigits: 2};
  }

  componentWillReceiveProps(newProps) {
    const { LoggedInUser } = newProps;
    const { expense } = this.props;
    if (LoggedInUser && this.state.mode === 'summary') {
      let mode = 'summary';
      if (LoggedInUser) {
        if (expense.status === 'PENDING' && LoggedInUser.canApproveExpense(expense)) {
          mode = 'details';
        }
        if (expense.status === 'APPROVED' && LoggedInUser.canPayExpense(expense)) {
          mode = 'details';
        }
      }

      this.setState({ mode });
    }
  }

  toggleDetails() {
    this.setState({
      mode: this.state.mode === 'details' ? 'summary' : 'details',
    });
  }

  cancelEdit() {
    this.setState({ modified: false, mode: 'details' });
  }

  edit() {
    this.setState({ modified: false, mode: 'edit' });
  }

  toggleEdit() {
    this.state.mode === 'edit' ? this.cancelEdit() : this.edit();
  }

  handleChange(expense) {
    this.setState({ modified: true, expense });
  }

  async save() {
    const expense = {
      id: this.props.expense.id,
      ...this.state.expense
    }
    const res = await this.props.editExpense(expense);
    this.setState({ modified: false, mode: 'details' });
  }

  render() {
    const {
      intl,
      collective,
      expense,
      includeHostedCollectives,
      LoggedInUser,
      editable
    } = this.props;

    const title = expense.description;
    const status = expense.status.toLowerCase();

    return (
      <div className={`expense ${status} ${this.state.mode}View`}>

          <a href={`/${expense.fromCollective.slug}`} title={expense.fromCollective.name}>
            <Avatar src={expense.fromCollective.image} key={expense.fromCollective.id} radius={40} />

            {/* should link to `/${collective.slug}/expenses/${expense.uuid}` once we have a page for it */}
              {capitalize(title)}

            <FormattedDate value={expense.incurredAt} day="numeric" month="numeric" /> |
            { includeHostedCollectives &&
              <Link route={`/${expense.collective.slug}`}>{expense.collective.slug}</Link> (balance: {formatCurrency(expense.collective.stats.balance, expense.collective.currency)}) |
            }
            {intl.formatMessage(this.messages[status])} |
            {` ${capitalize(expense.category)}`}
            { editable && LoggedInUser && LoggedInUser.canEditExpense(expense) &&
               | {intl.formatMessage(this.messages[`${this.state.mode === 'edit' ? 'cancelEdit' : 'edit'}`])}
            }
            { this.state.mode !== 'edit' &&
               | {intl.formatMessage(this.messages[`${this.state.mode === 'details' ? 'closeDetails' : 'viewDetails'}`])}
            }

          <ExpenseDetails
            LoggedInUser={LoggedInUser}
            expense={expense}
            collective={collective}
            onChange={this.handleChange}
            mode={this.state.mode}
            />

            { this.state.mode === 'edit' && this.state.modified &&

                  <SmallButton className="primary" onClick={this.save}><FormattedMessage id="expense.save" defaultMessage="save" /></SmallButton>

            }
            { this.state.mode !== 'edit' && LoggedInUser && LoggedInUser.canApproveExpense(expense) &&

                { expense.status === 'APPROVED' && LoggedInUser.canPayExpense(expense) &&
                  <PayExpenseBtn
                    expense={expense}
                    disabled={!this.props.allowPayAction}
                    lock={this.props.lockPayAction}
                    unlock={this.props.unlockPayAction}
                    />
                }
                { expense.status !== 'APPROVED' && expense.status !== 'PAID' && <ApproveExpenseBtn id={expense.id} /> }
                { expense.status !== 'REJECTED' && expense.status !== 'PAID' && <RejectExpenseBtn id={expense.id} /> }

            }

          <FormattedNumber
            value={expense.amount / 100}
            currency={expense.currency}
            {...this.currencyStyle}
            />

    );
  }
}

const editExpenseQuery = gql`
mutation editExpense($expense: ExpenseInputType!) {
  editExpense(expense: $expense) {
    id
    description
    amount
    attachment
    category
    privateMessage
    payoutMethod
    status
  }
}
`;

const addMutation = graphql(editExpenseQuery, {
props: ( { mutate }) => ({
  editExpense: async (expense) => {
    return await mutate({ variables: { expense } })
  }
})
});

export default withIntl(addMutation(Expense));<|endoftext|>


<commit_before>/*!
 * Copyright 2017 Webdetails, a Hitachi Vantara company. All rights reserved.
 *
 * This software was developed by Webdetails and is provided under the terms
 * of the Mozilla Public License, Version 2.0, or any later version. You may not use
 * this file except in compliance with the license. If you need a copy of the license,
 * please go to http://mozilla.org/MPL/2.0/. The Initial Developer is Webdetails.
 *
 * Software distributed under the Mozilla Public License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
 * the license for the specific language governing your rights and limitations.
 */

define([
    './BaseQuery',
    './SolrQuery.ext',
    '../dashboard/Dashboard.query',
    '../Logger'
], function(BaseQuery, SolrQueryExt, Dashboard, Logger) {
  "use strict";

  var SOLR_TYPE = "solr";

  var solrQueryOpts = {
    name: "solr",

    label: "Apache Solr Query",

    defaults: {
      endpoint: SolrQueryExt.getEndpoint(),
      collection: SolrQueryExt.getCollection(),
      ajaxOptions: {
        async: true,
        xhrFields: {
          withCredentials: true
        }
      }
    },

    // ---- methods

    /** @override */
    init: function(options) {
      Logger.log("Apache Solr - Initializing Apache Solr Query");

      var endpoint = options.endpoint != null ? options.endpoint : this.getOption("endpoint");
      var collection = options.collection != null ? options.collection : this.getOption("collection");

      if ( !endpoint.endsWith('/') ) {
        endpoint = endpoint + "/";
      }

      this.setOption( 'url', endpoint + collection );
    },

    /** @override */
    buildQueryDefinition: function(/* overrides */) {
      Logger.log("Apache Solr - Building Query Definition");

      return {
        url: this.getOption('url')
      };
    }
  };

  Dashboard.registerGlobalQuery(SOLR_TYPE, solrQueryOpts);
});
<commit_msg>[BACKLOG-21176] added a new success handler to SolrQuery<commit_after>/*!
 * Copyright 2017 Webdetails, a Hitachi Vantara company. All rights reserved.
 *
 * This software was developed by Webdetails and is provided under the terms
 * of the Mozilla Public License, Version 2.0, or any later version. You may not use
 * this file except in compliance with the license. If you need a copy of the license,
 * please go to http://mozilla.org/MPL/2.0/. The Initial Developer is Webdetails.
 *
 * Software distributed under the Mozilla Public License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
 * the license for the specific language governing your rights and limitations.
 */

define([
    './BaseQuery',
    './SolrQuery.ext',
    '../dashboard/Dashboard.query',
    '../Logger',
    'amd!../lib/lodash'
], function(BaseQuery, SolrQueryExt, Dashboard, Logger,_) {
  "use strict";

  var SOLR_TYPE = "solr";

  var solrQueryOpts = {
    name: "solr",

    label: "Apache Solr Query",

    defaults: {
      endpoint: SolrQueryExt.getEndpoint(),
      collection: SolrQueryExt.getCollection(),
      successCallback: function (data) {
        console.log("i'm here: ",data);
      },
      ajaxOptions: {
        async: true,
        type: "GET",
        xhrFields: {
          withCredentials: true
        }
      }
    },

    // ---- methods

    /** @override */
    init: function(options) {
      Logger.log("Apache Solr - Initializing Apache Solr Query");

      var endpoint = options.endpoint !== null ? options.endpoint : this.getOption("endpoint");
      var collection = options.collection !== null ? options.collection : this.getOption("collection");
      var requestHandler = options.requestHandler !== null ? options.requestHandler : this.getOption("requestHandler");
      var responseType = options.responseType !== null ? options.responseType : this.getOption("responseType");
      var solrQuery = options.solrQuery !== null ? options.solrQuery : this.getOption("solrQuery");

      if ( !endpoint.endsWith('/') ) {
        endpoint = endpoint + "/";
      }

      this.setOption( 'url', endpoint + collection + '/' + requestHandler + '?q=' + solrQuery + '&wt=' + responseType);
    },

    /** @override */
    buildQueryDefinition: function(/* overrides */) {
      Logger.log("Apache Solr - Building Query Definition");

      return {
        url: this.getOption('url')
      };
    },

    /** @override */
    getSuccessHandler: function(callback) {

      var baseSuccessCallback = this.base(callback);

      return function(json) {

        // Transform into CDA format
        var jsonObjects = JSON.parse(json);
        var soltResponse = jsonObjects.response;
        var docs = soltResponse.docs;

        /*
         *  TODO: This is a mock schema, replace with real schema from CDE editor
         **/
        var cdeSchema = {
          "columnNames":[
            "Winner",
            "WRank",
            "nested"
          ],
          "columnTypes":[
            "string",
            "int",
            "int"
          ],
          "columnPaths":[
            "Winner",
            "WRank",
            "nested.element.very.deep"
          ]
        }

        var cdaData = {
          "resultset":[],
          "metadata":[]
        }

        // Get Metadata
        var colIndex = 0;
        cdeSchema.columnNames.forEach(function(elem, index){
          var metadataRow = {
            "colName": elem,
            "colType": cdeSchema.columnTypes[index],
            "colIndex": colIndex++
          }
          cdaData.metadata.push(metadataRow);
        });

        // Get Resultset
        docs.forEach(function(docElem, docIndex){
          var docRow = [];
          cdeSchema.columnPaths.forEach(function(keyElem, keyIndex){
            var elem = docElem[keyElem];
            //deal with nesting
            if(elem === undefined){
              elem = _.get(docElem, keyElem);
            }
            docRow.push(Array.isArray(elem) ? elem[0] : elem)
          });
          cdaData.resultset.push(docRow);
        });

        // console.log(cdaData); // TODO: Delete, was used for debugging
        baseSuccessCallback(cdaData);
      };
    }
  };

  Dashboard.registerGlobalQuery(SOLR_TYPE, solrQueryOpts);
});
<|endoftext|>


<commit_before>chorus.Modal = chorus.views.Base.extend({
    constructorName: "Modal",
    focusSelector: "input:eq(0)",
    verticalPosition: 40, // distance of dialog from top of window

    launchModal: function() {
        if (chorus.modal && this !== chorus.modal) {
            chorus.modal.launchSubModal(this);
        } else {
            this.launchNewModal();
        }
    },

    launchNewModal:function () {
        this.render();
        $(document).one('reveal.facebox', _.bind(this.beginReveal, this));
        $.facebox(this.el);
        this.previousModal = chorus.modal;
        this.restore();
        this.showErrors();
    },

    launchSubModal: function(subModal) {
        this.ignoreFacebox();
        this.background();

        $.facebox.settings.inited = false;
        subModal.launchNewModal();
    },

    resize: function(windowWidth, windowHeight) {

    /* jshint ignore:start */
//     console.log ("modals.js RESIZE****** wd:" + windowWidth + " | ht:" + windowHeight);

// BEGIN: move .errors inside of .girdle
// TODO: fix when dialogs are redone

    $(".girdle").prepend($(".errors"));

// END: move the .errors

        var $facebox = $('#facebox');
        var $popup = $("#facebox .popup");

        var $window = $(window);

        var verticalPositionValue = this.verticalPosition;
//          if (typeof verticalPositionValue === "undefined") {
//              console.log ("verticalPositionValue undefined");
//              verticalPositionValue = $(window).verticalPositionValue;
//              console.log ("1 verticalPositionValue " + verticalPositionValue);
//              verticalPositionValue = $(document).verticalPositionValue;
//              console.log ("2 verticalPositionValue " + verticalPositionValue);
//         }

//     console.log ("modals.js > resize: windowHeight A:" + windowHeight);
        if (!windowHeight) windowHeight = $window.height();
//     console.log ("modals.js > resize: windowHeight B:" + windowHeight);
//     console.log ("modals.js > resize: this.verticalPosition 1:" + this.verticalPosition);
//     console.log ("modals.js > resize: this.verticalPosition 2:" + verticalPositionValue);

        //position the dialog vertically in the window
        // $facebox.css('top', this.verticalPosition + 'px');
        $facebox.css('top', verticalPositionValue + 'px');

// console.log ("modals.js > resize: top of facebox:" + $facebox.offset().top);

        //calculate max height based on current window
        // var popupHeight = windowHeight - this.verticalPosition * 2;
        var popupHeight = windowHeight - verticalPositionValue * 2;
        $popup.css("max-height", popupHeight + "px");
// console.log ("modals.js > resize: popupHeight:" + popupHeight);

        //calculate max height for interior content
        // 1st: find height of the dialog header and footer...
        var headerHeight = $("#dialog_header").height();
        var bottomHeight = $("#dialog_bottom").height();

        // ...if errors are outside of .girdle, then chek if .errors has any height
        // if YES, then that height will need to be added to the overall available height
        var errorHeight = $(".dialog .errors").height();
        errorHeight = isNaN (errorHeight) ? errorHeight : 0;

        // 2nd: figure out height inside .girdle
        // and account for the visual padding
        var girdleHeight = $(".girdle").height();
        var girdleVerticalPadding = $(".girdle").innerHeight() - girdleHeight;

// console.log ("modals// .js > resize: girdleVerticalPadding:" + girdleVerticalPadding);

        // 3rd: max height will be (total height of popup) - (the sum of the other heights)
        var maxInteriorHeight = popupHeight - (headerHeight + bottomHeight + errorHeight + girdleVerticalPadding);
// console.log ("modals.js > resize: maxInteriorHeight A:" + maxInteriorHeight);

       //  maxInteriorHeight = maxInteriorHeight- girdleVerticalPadding;
// console.log ("modals.js > resize: maxInteriorHeight B:" + maxInteriorHeight);

        var $dialogInteriorContent = $("#dialog_content .girdle");
        $dialogInteriorContent.css("max-height", maxInteriorHeight + "px");

//         console.log ("modals.js > resize: max-height:" + maxInteriorHeight);
//         console.log ("modals.js > resize: error-height:" + errorHeight);

/* jshint ignore:end */
    },

    preRender: function() {
        var result = this._super('preRender', arguments);

        $(window).resize(this.resize);
        $(window).resize(this.centerHorizontally);

        this.preventScrollingBody();
        return result;
    },

    centerHorizontally: function () {
        $('#facebox').css('left', $(window).width() / 2 - ($('#facebox .popup').width() / 2));
    },

    postRender: function() {
        this._super("postRender");
        this.centerHorizontally();
    },

    makeModel:function (options) {
        if (options && options.pageModel) {
            this.pageModel = options.pageModel;
            this.model = this.model || this.pageModel;
        }
    },

    closeModal:function (success) {
        $(document).trigger("close.facebox");
        if (success === true) {
            $(document).trigger("close.faceboxsuccess");
        }
    },

    keydownHandler:function (e) {
        if (e.keyCode === 27) {
            e.preventDefault();
            this.escapePressed();
        }
    },

    escapePressed:function () {
        this.closeModal();
    },

    modalClosed:function () {
        if (this === chorus.modal) {
            this.close();
            $("#facebox").remove();
            $.facebox.settings.inited = false;
            chorus.PageEvents.trigger("modal:closed", this);

            delete chorus.modal;
            this.ignoreFacebox();

            if (this.previousModal) {
                this.previousModal.restore();
            } else {
                this.restoreScrollingBody();
            }
        }
        this.teardown();
    },

    restore: function () {
        chorus.modal = this;
        this.foreground();
        this.listenToFacebox();
        this.recalculateScrolling();
        _.defer(this.resize);
    },

    foreground: function () {
        $("#facebox-" + this.faceboxCacheId).attr("id", "facebox").removeClass("hidden");
        $("#facebox_overlay-" + this.faceboxCacheId).attr("id", "facebox_overlay");
        this.resize();
    },

    background: function () {
        this.faceboxCacheId = Math.floor((Math.random()*1e8)+1).toString();
        $("#facebox").attr("id", "facebox-" + this.faceboxCacheId).addClass("hidden");
        $("#facebox_overlay").attr("id", "facebox_overlay-" + this.faceboxCacheId);
    },

    listenToFacebox: function() {
        this.boundModalClosed = _.bind(this.modalClosed, this);
        this.boundKeyDownHandler = _.bind(this.keydownHandler, this);
        $(document).one("close.facebox", this.boundModalClosed);
        $(document).bind("keydown.facebox", this.boundKeyDownHandler);
    },

    ignoreFacebox: function() {
        $(document).unbind("close.facebox", this.boundModalClosed);
        $(document).unbind("keydown.facebox", this.boundKeyDownHandler);
        delete this.boundModalClosed;
        delete this.boundKeyDownHandler;
    },

    preventScrollingBody: function() {
        $("body").css("overflow", "hidden");
    },

    restoreScrollingBody: function() {
        $("body").css("overflow", "visible");
    },

    close:$.noop,
    revealed: $.noop,

    beginReveal: function() {
        this.revealed();
        if (this.focusSelector) {
            this.$(this.focusSelector).focus();
        }
    },

    saveFailed: function(model) {
        this.$("button.submit").stopLoading();
        this.$("button.cancel").prop("disabled", false);
        if(model) {
            this.showErrors(model);
        } else {
            this.showErrors();
        }
    }
});

if (window.jasmine) {
    chorus.Modal.prototype.preventScrollingBody = $.noop;
    chorus.Modal.prototype.restoreScrollingBody = $.noop;
}
<commit_msg>DEV-11412 Corrects error div being added twice.<commit_after>chorus.Modal = chorus.views.Base.extend({
    constructorName: "Modal",
    focusSelector: "input:eq(0)",
    verticalPosition: 40, // distance of dialog from top of window

    launchModal: function() {
        if (chorus.modal && this !== chorus.modal) {
            chorus.modal.launchSubModal(this);
        } else {
            this.launchNewModal();
        }
    },

    launchNewModal:function () {
        this.render();
        $(document).one('reveal.facebox', _.bind(this.beginReveal, this));
        $.facebox(this.el);
        this.previousModal = chorus.modal;
        this.restore();
        this.showErrors();
    },

    launchSubModal: function(subModal) {
        this.ignoreFacebox();
        this.background();

        $.facebox.settings.inited = false;
        subModal.launchNewModal();
    },

    resize: function(windowWidth, windowHeight) {

    /* jshint ignore:start */
//     console.log ("modals.js RESIZE****** wd:" + windowWidth + " | ht:" + windowHeight);

// BEGIN: move .errors inside of .girdle
// TODO: fix when dialogs are redone

// $(".girdle").prepend($(".errors"));

// END: move the .errors

        var $facebox = $('#facebox');
        var $popup = $("#facebox .popup");

        var $window = $(window);

        var verticalPositionValue = this.verticalPosition;
//          if (typeof verticalPositionValue === "undefined") {
//              console.log ("verticalPositionValue undefined");
//              verticalPositionValue = $(window).verticalPositionValue;
//              console.log ("1 verticalPositionValue " + verticalPositionValue);
//              verticalPositionValue = $(document).verticalPositionValue;
//              console.log ("2 verticalPositionValue " + verticalPositionValue);
//         }

//     console.log ("modals.js > resize: windowHeight A:" + windowHeight);
        if (!windowHeight) windowHeight = $window.height();
//     console.log ("modals.js > resize: windowHeight B:" + windowHeight);
//     console.log ("modals.js > resize: this.verticalPosition 1:" + this.verticalPosition);
//     console.log ("modals.js > resize: this.verticalPosition 2:" + verticalPositionValue);

        //position the dialog vertically in the window
        // $facebox.css('top', this.verticalPosition + 'px');
        $facebox.css('top', verticalPositionValue + 'px');

// console.log ("modals.js > resize: top of facebox:" + $facebox.offset().top);

        //calculate max height based on current window
        // var popupHeight = windowHeight - this.verticalPosition * 2;
        var popupHeight = windowHeight - verticalPositionValue * 2;
        $popup.css("max-height", popupHeight + "px");
// console.log ("modals.js > resize: popupHeight:" + popupHeight);

        //calculate max height for interior content
        // 1st: find height of the dialog header and footer...
        var headerHeight = $("#dialog_header").height();
        var bottomHeight = $("#dialog_bottom").height();

        // ...if errors are outside of .girdle, then chek if .errors has any height
        // if YES, then that height will need to be added to the overall available height
        var errorHeight = $(".dialog .errors").height();
        errorHeight = isNaN (errorHeight) ? errorHeight : 0;

        // 2nd: figure out height inside .girdle
        // and account for the visual padding
        var girdleHeight = $(".girdle").height();
        var girdleVerticalPadding = $(".girdle").innerHeight() - girdleHeight;

// console.log ("modals// .js > resize: girdleVerticalPadding:" + girdleVerticalPadding);

        // 3rd: max height will be (total height of popup) - (the sum of the other heights)
        var maxInteriorHeight = popupHeight - (headerHeight + bottomHeight + errorHeight + girdleVerticalPadding);
// console.log ("modals.js > resize: maxInteriorHeight A:" + maxInteriorHeight);

       //  maxInteriorHeight = maxInteriorHeight- girdleVerticalPadding;
// console.log ("modals.js > resize: maxInteriorHeight B:" + maxInteriorHeight);

        var $dialogInteriorContent = $("#dialog_content .girdle");
        $dialogInteriorContent.css("max-height", maxInteriorHeight + "px");

//         console.log ("modals.js > resize: max-height:" + maxInteriorHeight);
//         console.log ("modals.js > resize: error-height:" + errorHeight);

/* jshint ignore:end */
    },

    preRender: function() {
        var result = this._super('preRender', arguments);

        $(window).resize(this.resize);
        $(window).resize(this.centerHorizontally);

        this.preventScrollingBody();
        return result;
    },

    centerHorizontally: function () {
        $('#facebox').css('left', $(window).width() / 2 - ($('#facebox .popup').width() / 2));
    },

    postRender: function() {
        this._super("postRender");
        this.centerHorizontally();
    },

    makeModel:function (options) {
        if (options && options.pageModel) {
            this.pageModel = options.pageModel;
            this.model = this.model || this.pageModel;
        }
    },

    closeModal:function (success) {
        $(document).trigger("close.facebox");
        if (success === true) {
            $(document).trigger("close.faceboxsuccess");
        }
    },

    keydownHandler:function (e) {
        if (e.keyCode === 27) {
            e.preventDefault();
            this.escapePressed();
        }
    },

    escapePressed:function () {
        this.closeModal();
    },

    modalClosed:function () {
        if (this === chorus.modal) {
            this.close();
            $("#facebox").remove();
            $.facebox.settings.inited = false;
            chorus.PageEvents.trigger("modal:closed", this);

            delete chorus.modal;
            this.ignoreFacebox();

            if (this.previousModal) {
                this.previousModal.restore();
            } else {
                this.restoreScrollingBody();
            }
        }
        this.teardown();
    },

    restore: function () {
        chorus.modal = this;
        this.foreground();
        this.listenToFacebox();
        this.recalculateScrolling();
        _.defer(this.resize);
    },

    foreground: function () {
        $("#facebox-" + this.faceboxCacheId).attr("id", "facebox").removeClass("hidden");
        $("#facebox_overlay-" + this.faceboxCacheId).attr("id", "facebox_overlay");
        this.resize();
    },

    background: function () {
        this.faceboxCacheId = Math.floor((Math.random()*1e8)+1).toString();
        $("#facebox").attr("id", "facebox-" + this.faceboxCacheId).addClass("hidden");
        $("#facebox_overlay").attr("id", "facebox_overlay-" + this.faceboxCacheId);
    },

    listenToFacebox: function() {
        this.boundModalClosed = _.bind(this.modalClosed, this);
        this.boundKeyDownHandler = _.bind(this.keydownHandler, this);
        $(document).one("close.facebox", this.boundModalClosed);
        $(document).bind("keydown.facebox", this.boundKeyDownHandler);
    },

    ignoreFacebox: function() {
        $(document).unbind("close.facebox", this.boundModalClosed);
        $(document).unbind("keydown.facebox", this.boundKeyDownHandler);
        delete this.boundModalClosed;
        delete this.boundKeyDownHandler;
    },

    preventScrollingBody: function() {
        $("body").css("overflow", "hidden");
    },

    restoreScrollingBody: function() {
        $("body").css("overflow", "visible");
    },

    close:$.noop,
    revealed: $.noop,

    beginReveal: function() {
        this.revealed();
        if (this.focusSelector) {
            this.$(this.focusSelector).focus();
        }
    },

    saveFailed: function(model) {
        this.$("button.submit").stopLoading();
        this.$("button.cancel").prop("disabled", false);
        if(model) {
            this.showErrors(model);
        } else {
            this.showErrors();
        }
    }
});

if (window.jasmine) {
    chorus.Modal.prototype.preventScrollingBody = $.noop;
    chorus.Modal.prototype.restoreScrollingBody = $.noop;
}
<|endoftext|>


<commit_before>//     editabletext.js 1.0
//     (c) 2012 Juan Batiz-Benet.
//     Backbone.js component
//     Acorn is freely distributable under the MIT license.

(function() {

  // Setup
  // -----

  // Establish the root object, `window` in browser, or `global` on server.
  var root = this;

  // For our purposes, jQuery, Zepto, or Ender owns the `$` variable.
  var $ = root.jQuery || root.Zepto || root.ender;

  // Underscore and Backbone
  var _ = root._;
  var Backbone = root.Backbone;

  // Define our root Object
  var EditableTextCmp = {};

  // Register components
  if (Backbone.components === undefined)
    Backbone.components = {};
  Backbone.components.EditableTextCmp = EditableTextCmp;

  // Util functions

  var _lastId = 1;
  EditableTextCmp.nextId = function() {
    return 'editabletext' + _lastId++;
  };

  // Setup template.
  EditableTextCmp.template = _.template("\
  <div class='editabletext' id='<%= id %>'>\
    <div id='<%= id %>_text'>\
      <%= html %>\
      <% if (addToggle) { %>edit<% } %>\

\
    <% if (multiline) { %>\
    <textarea id='<%= id %>_edit' style='display: none;'><%= text %></textarea>\
    <% } else { %>\
      <input type='text' id='<%= id %>_edit'\
        style='display: none;' value='<%= text %>' />\
    <% } %>\

\
  ");

  // Setup view.
  EditableTextCmp.View = Backbone.View.extend({

    template: EditableTextCmp.template,

    // Delegated events for creating new items, and clearing completed ones.
    events: {
      "keypress textarea":  "saveOnEnter",
      "keypress input":  "saveOnEnter",
      "click #toggle": "toggle"
    },

    initialize: function() {
      _.bindAll(this, 'saveOnEnter', 'toggle');

      if (!this.options.textFn) {
        throw new Error("EditableTextCmp requires option textFn.");
      }

      this.options.html = this.options.html || function(_) { return _; }
      this.options.multiline = !!this.options.multiline;
      this.options.addToggle = !!this.options.addToggle;
      this.options.characterLimit = parseInt(this.options.characterLimit) || -1;
      this.options.enterSaves = this.options.enterSaves || true;
      this.options.id = this.options.id || EditableTextCmp.nextId();
    },

    render: function() {
      var text_ = this.options.textFn();

      $(this.el).html(this.template({
        id: this.options.id,
        text: text_ || ' ',
        html: this.html(text_ || ' '),
        multiline: this.options.multiline,
        addToggle: this.isEditable() && this.options.addToggle
      }));

      if (this.options.help) {
        this.find(".editabletext > #edit").popover(this.options.help);
      }

      if (this.isEditable() && this.options.externalToggle) {
        $(this.options.externalToggle).text('edit');

        var event = 'click.edit_' + this.options.id;
        $(this.options.externalToggle).off(event);
        $(this.options.externalToggle).on(event, this.toggle);
      }
    },

    find: function(sel) {
      // add the randomly generated id in between.
      var id = $(this.el).find('.editabletext').attr('id');
      return $(this.el).find(sel.replace(/#/g, '#' + id + '_'));
    },

    // add any transforms from text to display html.
    html: function(text) {
      text = this.options.html(text);
      text = text.replace(/\n/gi, '
');
      return text;
    },

    isEditing: false,
    isEditable: function() {
      switch (typeof this.options.editable) {
        case 'function': return this.options.editable();
        case 'undefined': return true;
        default: return !!this.options.editable;
      }
    },

    edit: function() {
      if (!this.isEditable())
        return;

      var field = this.find(".editabletext > #edit");
      var text = this.find(".editabletext > #text");

      var leftPad = (this.options.multiline ? 5 : 6);
      var topPad = 5;

      field.css("width", text.css("width") || '100');
      field.css("height", text.css("height") || '25');
      field.css("font", text.css("font"));
      field.css("margin-left", parseInt(text.css("margin-left")) - leftPad);
      field.css("margin-top", parseInt(text.css("margin-top")) - topPad);

      text.hide();
      field.show();
      field.focus();
      field.value = field.value; // to move cursor to end.

      this.isEditing = true;

      if (this.options.externalToggle)
        $(this.options.externalToggle).text('save');

      if (this.options.onEdit)
        this.options.onEdit();
    },

    saveText: function() {
      return this.find(".editabletext > #edit").val()
    },

    save: function() {
      if (!this.isEditable())
        return;

      var text = this.saveText();

      // close popover
      this.find(".editabletext > #edit").popover('hide');

      // Attempt to validate
      if (this.options.validate) {
        var result = this.options.validate(text);
        if (result) {
          // validation failed. show help, and return
          this.find(".editabletext > #edit").popover(result);
          this.find(".editabletext > #edit").popover('show');
          return;
        }
      }

      // save and render
      this.options.textFn(text);
      this.render();
      this.isEditing = false;

      if (this.options.externalToggle)
        $(this.options.externalToggle).text('edit');

      if (this.options.onSave)
        this.options.onSave();
    },

    toggle: function() {
      this.isEditing ? this.save() : this.edit();
    },

    saveOnEnter: function(e) {
      if (e.keyCode == 13 && this.isEditing &&
          !!this.options.enterSaves && !e.shiftKey) {
        this.save();
        return false;
      }

      var limit = this.options.characterLimit;
      var cleft = limit - this.saveText().length;
      if (limit >= 0) {
        $(this.el).tooltip({title: cleft + ' characters left.'});
      }
      if (limit >= 0 && cleft <= 0) {
        // over the limit.
        return false;
      }

    },

    alert: function(text) {
      this.find('.editabletext > #edit').css("class", "error");
      this.find('.editabletext > #edit').popover({
        title: "Error: invalid text.",
        body: text,
        trigger: 'manual',
      });
    },

  });

  return EditableTextCmp;
}).call(this);
<commit_msg>frontend: changed editabletext to use a tooltip to validate.<commit_after>//     editabletext.js 1.0
//     (c) 2012 Juan Batiz-Benet.
//     Backbone.js component
//     Acorn is freely distributable under the MIT license.

(function() {

  // Setup
  // -----

  // Establish the root object, `window` in browser, or `global` on server.
  var root = this;

  // For our purposes, jQuery, Zepto, or Ender owns the `$` variable.
  var $ = root.jQuery || root.Zepto || root.ender;

  // Underscore and Backbone
  var _ = root._;
  var Backbone = root.Backbone;

  // Define our root Object
  var EditableTextCmp = {};

  // Register components
  if (Backbone.components === undefined)
    Backbone.components = {};
  Backbone.components.EditableTextCmp = EditableTextCmp;

  // Util functions

  var _lastId = 1;
  EditableTextCmp.nextId = function() {
    return 'editabletext' + _lastId++;
  };

  // Setup template.
  EditableTextCmp.template = _.template("\
  <div class='editabletext' id='<%= id %>'>\
    <div id='<%= id %>_text'>\
      <%= html %>\
      <% if (addToggle) { %>edit<% } %>\

\
    <% if (multiline) { %>\
    <textarea id='<%= id %>_edit' style='display: none;'><%= text %></textarea>\
    <% } else { %>\
      <input type='text' id='<%= id %>_edit'\
        style='display: none;' value='<%= text %>' />\
    <% } %>\

\
  ");

  // Setup view.
  EditableTextCmp.View = Backbone.View.extend({

    template: EditableTextCmp.template,

    // Delegated events for creating new items, and clearing completed ones.
    events: {
      "keypress textarea":  "saveOnEnter",
      "keypress input":  "saveOnEnter",
      "click #toggle": "toggle"
    },

    initialize: function() {
      _.bindAll(this, 'saveOnEnter', 'toggle');

      if (!this.options.textFn) {
        throw new Error("EditableTextCmp requires option textFn.");
      }

      this.options.html = this.options.html || function(_) { return _; }
      this.options.multiline = !!this.options.multiline;
      this.options.addToggle = !!this.options.addToggle;
      this.options.characterLimit = parseInt(this.options.characterLimit) || -1;
      this.options.enterSaves = this.options.enterSaves || true;
      this.options.id = this.options.id || EditableTextCmp.nextId();
    },

    render: function() {
      var text_ = this.options.textFn();

      $(this.el).html(this.template({
        id: this.options.id,
        text: text_ || ' ',
        html: this.html(text_ || ' '),
        multiline: this.options.multiline,
        addToggle: this.isEditable() && this.options.addToggle
      }));

      if (this.options.help) {
        this.find(".editabletext > #edit").popover(this.options.help);
      }

      if (this.isEditable() && this.options.externalToggle) {
        $(this.options.externalToggle).text('edit');

        var event = 'click.edit_' + this.options.id;
        $(this.options.externalToggle).off(event);
        $(this.options.externalToggle).on(event, this.toggle);
      }
    },

    find: function(sel) {
      // add the randomly generated id in between.
      var id = $(this.el).find('.editabletext').attr('id');
      return $(this.el).find(sel.replace(/#/g, '#' + id + '_'));
    },

    // add any transforms from text to display html.
    html: function(text) {
      text = this.options.html(text);
      text = text.replace(/\n/gi, '
');
      return text;
    },

    isEditing: false,
    isEditable: function() {
      switch (typeof this.options.editable) {
        case 'function': return this.options.editable();
        case 'undefined': return true;
        default: return !!this.options.editable;
      }
    },

    edit: function() {
      if (!this.isEditable())
        return;

      var field = this.find(".editabletext > #edit");
      var text = this.find(".editabletext > #text");

      var leftPad = (this.options.multiline ? 5 : 6);
      var topPad = 5;

      field.css("width", text.css("width") || '100');
      field.css("height", text.css("height") || '25');
      field.css("font", text.css("font"));
      field.css("margin-left", parseInt(text.css("margin-left")) - leftPad);
      field.css("margin-top", parseInt(text.css("margin-top")) - topPad);

      text.hide();
      field.show();
      field.focus();
      field.value = field.value; // to move cursor to end.

      this.isEditing = true;

      if (this.options.externalToggle)
        $(this.options.externalToggle).text('save');

      if (this.options.onEdit)
        this.options.onEdit();
    },

    saveText: function() {
      return this.find(".editabletext > #edit").val()
    },

    save: function() {
      if (!this.isEditable())
        return;

      var text = this.saveText();

      // close popover
      this.find(".editabletext > #edit").tooltip('hide');

      // Attempt to validate
      if (this.options.validate) {
        var result = this.options.validate(text);
        if (result) {

          // validation failed. show help, and return
          this.find(".editabletext > #edit").tooltip({ title: result });
          this.find(".editabletext > #edit").tooltip('show');
          return;
        }
      }

      // save and render
      this.options.textFn(text);
      this.render();
      this.isEditing = false;

      if (this.options.externalToggle)
        $(this.options.externalToggle).text('edit');

      if (this.options.onSave)
        this.options.onSave();
    },

    toggle: function() {
      this.isEditing ? this.save() : this.edit();
    },

    saveOnEnter: function(e) {
      if (e.keyCode == 13 && this.isEditing &&
          !!this.options.enterSaves && !e.shiftKey) {
        this.save();
        return false;
      }

      var limit = this.options.characterLimit;
      var cleft = limit - this.saveText().length;
      if (limit >= 0) {
        $(this.el).tooltip({title: cleft + ' characters left.'});
      }
      if (limit >= 0 && cleft <= 0) {
        // over the limit.
        return false;
      }

    },

    alert: function(text) {
      this.find('.editabletext > #edit').css("class", "error");
      this.find('.editabletext > #edit').popover({
        title: "Error: invalid text.",
        body: text,
        trigger: 'manual',
      });
    },

  });

  return EditableTextCmp;
}).call(this);
<|endoftext|>


<commit_before>$(document).ready(function() {
  showSequence(id);
  displayComments();

  $(".js-next").on("click", function() {
    scrollSequence();
  });

  $('.comment_form').on('submit', function(event) {
    var valuesToSubmit = $(this).serialize();
    $.ajax({
      type: "POST",
      url: $(this).attr('action'),
      data: valuesToSubmit,
      dataType: "json",
      success: function(response) {
        console.log(response)
        addComment(response);
      },
      error: function(xhr, textStatus, errorThrown) {
      }
    });
    event.preventDefault();
  });
});

var id = parseInt(window.location.pathname.split("/")[2])

function showSequence(id) {
  $.get('/sequences/' + id + '.json', function(data) {
    var id = data["id"];
    $("#sequence-title").html('"' + data.title + '"')
    $("#sequence-difficulty").html("Difficulty Rating: " + data.difficulty)
    $("#repititions").html("For " + data.repititions + " rounds, repeat the following sequence of poses:")
    listPoses(data);
  })
};

function listPoses(data){
  poses = data.poses
  for (var i = 0; i < poses.length; i++){
    $("#poses").append('
' + poses[i].name + poses[i].description + '
');
  }
};

function scrollSequence(){
  $.get('/sequence_ids', function(data) {
    debugger;
    var idIndex = data.indexOf(id);
    if (data[idIndex + 1] === undefined) {
      nextId = data[0];
    } else {
      var nextId = data[idIndex + 1];
    }
    window.location.href = '/sequences/' + nextId;
    $(".js-next").attr("data-id", nextId);
  })
};

function displayComments() {
  $.get('/sequences/' + id + '.json', function(data) {
    for (var i = 0; i < data.comments.length; i++) {
      var date = new Date(data.comments[i].created_at)
      $("#display_comments").append('
' + data.comments[i].user.username + " says: " + data.comments[i].content + '
');
      $("#display_comments").append('
' + date.toUTCString() + '
')
    }
  });
}

function addComment(data){
  var response = data;
  $.get('/sequences/' + id + '.json', function(data) {
    for (var i = 0; i < data.comments.length; i++) {
      if (data.comments[i].id == response.id) {
        var comment = new Comment(data.comments[i])
        comment.appendToDOM()
      }
    }
  })
}

function Comment(data){
  this.content = data.content;
  this.created_at = data.created_at;
  this.id = data.id;
  this.user = data.user;
}

Comment.prototype.appendToDOM = function() {
  var date = new Date(this.created_at)
  $("#display_comments").append('
' + this.user.username + " says: " + this.content + '
');
  $("#display_comments").append('
' + date.toUTCString() + '
')
  $('.comment_form').val('');
}
<commit_msg>extended comment date to seperate function<commit_after>$(document).ready(function() {
  showSequence(id);
  displayComments();

  $(".js-next").on("click", function() {
    scrollSequence();
  });

  $('.comment_form').on('submit', function(event) {
    event.preventDefault();
    var valuesToSubmit = $(this).serialize();
    $.ajax({
      type: "POST",
      url: $(this).attr('action'),
      data: valuesToSubmit,
      dataType: "json",
      success: function(response) {
        addComment(response);
      },
      error: function(xhr, textStatus, errorThrown) {
      }
    });
  });
});

var id = parseInt(window.location.pathname.split("/")[2])

function showSequence(id) {
  $.get('/sequences/' + id + '.json', function(data) {
    var id = data["id"];
    $("#sequence-title").html('"' + data.title + '"')
    $("#sequence-difficulty").html("Difficulty Rating: " + data.difficulty)
    $("#repititions").html("For " + data.repititions + " rounds, repeat the following sequence of poses:")
    listPoses(data);
  })
};

function listPoses(data){
  poses = data.poses
  for (var i = 0; i < poses.length; i++){
    $("#poses").append('
' + poses[i].name + poses[i].description + '
');
  }
};

function scrollSequence(){
  $.get('/sequence_ids', function(data) {
    debugger;
    var idIndex = data.indexOf(id);
    if (data[idIndex + 1] === undefined) {
      nextId = data[0];
    } else {
      var nextId = data[idIndex + 1];
    }
    window.location.href = '/sequences/' + nextId;
    $(".js-next").attr("data-id", nextId);
  })
};

function displayComments() {
  $.get('/sequences/' + id + '.json', function(data) {
    for (var i = 0; i < data.comments.length; i++) {
      var date = new Date(data.comments[i].created_at)
      $("#display_comments").append('
' + data.comments[i].user.username + " says: " + data.comments[i].content + '
');
      $("#display_comments").append('
' + date.toUTCString() + '
')
    }
  });
}

function addComment(data){
  var response = data;
  $.get('/sequences/' + id + '.json', function(data) {
    for (var i = 0; i < data.comments.length; i++) {
      if (data.comments[i].id == response.id) {
        var comment = new Comment(data.comments[i])
        comment.appendToDOM()
      }
    }
  });
}

function Comment(data){
  this.content = data.content;
  this.created_at = data.created_at;
  this.id = data.id;
  this.user = data.user;
}

Comment.prototype.appendToDOM = function() {
  $("#display_comments").append('
' + this.user.username + " says: " + this.content + '
');
  $("#display_comments").append('
' + readableDate(this) + '
')
  $('.comment_form').val('');
}

function readableDate(data) {
  var getDate = new Date(data.created_at)
  return getDate.toUTCString()
}
<|endoftext|>


<commit_before>const assert = require('chai').assert;

const Plane = require('../lib/javascripts/plane');

describe('Plane', function() {
    context('with defaults', function() {
	var pizza = new Plane({type: "player", x: 100, y: 100});

	it('has a border', function() {
	    var border = pizza.border;
	    var expected_border = [[100, 100], [101, 100], [102, 100], [103, 100],
				   [104, 100], [105, 100],[106, 100],[107, 100],
				   [107, 101],[107, 102],[107, 103],[107, 104],
				   [107, 105],[107, 106],[107, 107],[107, 108],
				   [107, 109],[107, 110],[107, 111],[107, 112],
				   [107, 113],[107, 114],[107, 115],[107, 116],
				   [107, 117],[107, 118],[107, 119],[107, 120],
				   [107, 121],[107, 122],[107, 123],[107, 124],
				   [106, 124], [105, 124], [104, 124],
				   [103, 124], [102, 124],[101, 124],[100, 124],
				   [100, 123],[100, 122],[100, 121],[100, 120],
				   [100, 119],[100, 118],[100, 117],[100, 116],
				   [100, 115],[100, 114],[100, 113],[100, 112],
				   [100, 111],[100, 110],[100, 109],[100, 108],
				   [100, 107],[100, 106],[100, 105],[100, 104],
				   [100, 103],[100, 102],[100, 101]]
	    assert.notStrictEqual(expected_border, border);
	});
    });

});
<commit_msg>Fix fuselage has boundary test<commit_after>const assert = require('chai').assert;

const Plane = require('../lib/javascripts/plane');

describe('Plane', function() {
    context('with defaults', function() {
	let plane = new Plane({type: "player", x: 100, y: 100});

	it('has a border', function() {
	    plane.border = plane.makeBorder();
	    let border = plane.border;
	    let expected_border = [[100, 100], [101, 100], [102, 100], [103, 100],
				   [104, 100], [105, 100],[106, 100],[107, 100],
				   [107, 101],[107, 102],[107, 103],[107, 104],
				   [107, 105],[107, 106],[107, 107],[107, 108],
				   [107, 109],[107, 110],[107, 111],[107, 112],
				   [107, 113],[107, 114],[107, 115],[107, 116],
				   [107, 117],[107, 118],[107, 119],[107, 120],
				   [107, 121],[107, 122],[107, 123],[107, 124],
				   [106, 124], [105, 124], [104, 124],
				   [103, 124], [102, 124],[101, 124],[100, 124],
				   [100, 123],[100, 122],[100, 121],[100, 120],
				   [100, 119],[100, 118],[100, 117],[100, 116],
				   [100, 115],[100, 114],[100, 113],[100, 112],
				   [100, 111],[100, 110],[100, 109],[100, 108],
				   [100, 107],[100, 106],[100, 105],[100, 104],
				   [100, 103],[100, 102],[100, 101]]
	    assert.sameDeepMembers(expected_border, border);
	});
    });

});
<|endoftext|>