<commit_before>{% load i18n %}

/* globals
    blankImage,
    select2Thumbnail
*/

/* exported
    renderBuild,
    renderCompany,
    renderManufacturerPart,
    renderOwner,
    renderPart,
    renderPartCategory,
    renderStockItem,
    renderStockLocation,
    renderSupplierPart,
*/

/*
 * This file contains functions for rendering various InvenTree database models,
 * in particular for displaying them in modal forms in a 'select2' context.
 *
 * Each renderer is provided with three arguments:
 *
 * - name: The 'name' of the model instance in the referring model
 * - data: JSON data which represents the model instance. Returned via a GET request.
 * - parameters: The field parameters provided via an OPTIONS request to the endpoint.
 * - options: User options provided by the client
 */

// Should the ID be rendered for this string
function renderId(title, pk, parameters={}) {

    // Default = true
    var render = true;

    if ('render_pk' in parameters) {
        render = parameters['render_pk'];
    }

    if (render) {
        return `<span class='float-right'><small>${title}: ${pk}</small></span>`;
    } else {
        return '';
    }
}

// Renderer for "Company" model
// eslint-disable-next-line no-unused-vars
function renderCompany(name, data, parameters={}, options={}) {

    var html = select2Thumbnail(data.image);

    html += `<span><b>${data.name}</b></span> - <i>${data.description}</i>`;

    html += renderId('{% trans "Company ID" %}', data.pk, parameters);

    return html;
}

// Renderer for "StockItem" model
// eslint-disable-next-line no-unused-vars
function renderStockItem(name, data, parameters={}, options={}) {

    var image = blankImage();

    if (data.part_detail) {
        image = data.part_detail.thumbnail || data.part_detail.image || blankImage();
    }

    var render_part_detail = true;

    if ('render_part_detail' in parameters) {
        render_part_detail = parameters['render_part_detail'];
    }

    var part_detail = '';

    if (render_part_detail) {
        part_detail = `<img src='${image}' class='select2-thumbnail'><span>${data.part_detail.full_name}</span> - `;
    }

    var render_location_detail = false;

    if ('render_location_detail' in parameters) {
        render_location_detail = parameters['render_location_detail'];
    }

    var location_detail = '';

    if (render_location_detail && data.location_detail) {
        location_detail = ` <small>- (<em>${data.location_detail.name}</em>)</small>`;
    }

    var stock_detail = '';

    if (data.serial && data.quantity == 1) {
        stock_detail = `{% trans "Serial Number" %}: ${data.serial}`;
    } else if (data.quantity == 0) {
        stock_detail = `<span class='badge rounded-pill bg-danger'>{% trans "No Stock"% }</span>`;
    } else {
        stock_detail = `{% trans "Quantity" %}: ${data.quantity}`;
    }

    var html = `

        ${part_detail}
        ${stock_detail}
        ${location_detail}
        ${renderId('{% trans "Stock ID" %}', data.pk, parameters)}

    `;

    return html;
}

// Renderer for "StockLocation" model
// eslint-disable-next-line no-unused-vars
function renderStockLocation(name, data, parameters={}, options={}) {

    var level = '- '.repeat(data.level);

    var html = `<span>${level}${data.pathstring}</span>`;

    var render_description = true;

    if ('render_description' in parameters) {
        render_description = parameters['render_description'];
    }

    if (render_description && data.description) {
        html += ` - <i>${data.description}</i>`;
    }

    html += `<span class='float-right'><small>{% trans "Location ID" %}: ${data.pk}</small></span>`;

    return html;
}

// eslint-disable-next-line no-unused-vars
function renderBuild(name, data, parameters={}, options={}) {

    var image = null;

    if (data.part_detail && data.part_detail.thumbnail) {
        image = data.part_detail.thumbnail;
    }

    var html = select2Thumbnail(image);

    html += `<span><b>${data.reference}</b></span> - ${data.quantity} x ${data.part_detail.full_name}`;
    html += `<span class='float-right'><small>{% trans "Build ID" %}: ${data.pk}</span></span>`;

    html += `<p><i>${data.title}</i></p>`;

    return html;
}

// Renderer for "Part" model
// eslint-disable-next-line no-unused-vars
function renderPart(name, data, parameters={}, options={}) {

    var html = select2Thumbnail(data.image);

    html += ` <span>${data.full_name || data.name}</span>`;

    if (data.description) {
        html += ` - <i><small>${data.description}</small></i>`;
    }

    var stock_data = '';

    if (user_settings.PART_SHOW_QUANTITY_IN_FORMS) {
        stock_data = partStockLabel(data);
    }

    var extra = '';

    if (!data.active) {
        extra += `<span class='badge badge-right rounded-pill bg-danger'>{% trans "Inactive" %}</span>`;
    }

    html += `

            ${stock_data}
            ${extra}
            ${renderId('{% trans "Part ID" %}', data.pk, parameters)}

    `;

    return html;
}

// Renderer for "User" model
// eslint-disable-next-line no-unused-vars
function renderUser(name, data, parameters={}, options={}) {

    var html = `<span>${data.username}</span>`;

    if (data.first_name && data.last_name) {
        html += ` - <i>${data.first_name} ${data.last_name}</i>`;
    }

    return html;
}

// Renderer for "Owner" model
// eslint-disable-next-line no-unused-vars
function renderOwner(name, data, parameters={}, options={}) {

    var html = `<span>${data.name}</span>`;

    switch (data.label) {
    case 'user':
        html += `<span class='float-right fas fa-user'></span>`;
        break;
    case 'group':
        html += `<span class='float-right fas fa-users'></span>`;
        break;
    default:
        break;
    }

    return html;
}

// Renderer for "PurchaseOrder" model
// eslint-disable-next-line no-unused-vars
function renderPurchaseOrder(name, data, parameters={}, options={}) {

    var prefix = global_settings.PURCHASEORDER_REFERENCE_PREFIX;
    var html = `<span>${prefix}${data.reference}</span>`;

    var thumbnail = null;

    if (data.supplier_detail) {
        thumbnail = data.supplier_detail.thumbnail || data.supplier_detail.image;

        html += ' - ' + select2Thumbnail(thumbnail);
        html += `<span>${data.supplier_detail.name}</span>`;
    }

    if (data.description) {
        html += ` - <em>${data.description}</em>`;
    }

    html += renderId('{% trans "Order ID" %}', data.pk, parameters);

    return html;
}

// Renderer for "SalesOrder" model
// eslint-disable-next-line no-unused-vars
function renderSalesOrder(name, data, parameters={}, options={}) {

    var prefix = global_settings.SALESORDER_REFERENCE_PREFIX;
    var html = `<span>${prefix}${data.reference}</span>`;

    var thumbnail = null;

    if (data.customer_detail) {
        thumbnail = data.customer_detail.thumbnail || data.customer_detail.image;

        html += ' - ' + select2Thumbnail(thumbnail);
        html += `<span>${data.customer_detail.name}</span>`;
    }

    if (data.description) {
        html += ` - <em>${data.description}</em>`;
    }

    html += renderId('{% trans "Order ID" %}', data.pk, parameters);

    return html;
}

// Renderer for "SalesOrderShipment" model
// eslint-disable-next-line no-unused-vars
function renderSalesOrderShipment(name, data, parameters={}, options={}) {

    var so_prefix = global_settings.SALESORDER_REFERENCE_PREFIX;

    var html = `
    ${so_prefix}${data.order_detail.reference} - {% trans "Shipment" %} ${data.reference}

        {% trans "Shipment ID" %}: ${data.pk}

    `;

    return html;
}

// Renderer for "PartCategory" model
// eslint-disable-next-line no-unused-vars
function renderPartCategory(name, data, parameters={}, options={}) {

    var level = '- '.repeat(data.level);

    var html = `<span>${level}${data.pathstring}</span>`;

    if (data.description) {
        html += ` - <i>${data.description}</i>`;
    }

    html += `<span class='float-right'><small>{% trans "Category ID" %}: ${data.pk}</small></span>`;

    return html;
}

// eslint-disable-next-line no-unused-vars
function renderPartParameterTemplate(name, data, parameters={}, options={}) {

    var units = '';

    if (data.units) {
        units = ` [${data.units}]`;
    }

    var html = `<span>${data.name}${units}</span>`;

    return html;
}

// Renderer for "ManufacturerPart" model
// eslint-disable-next-line no-unused-vars
function renderManufacturerPart(name, data, parameters={}, options={}) {

    var manufacturer_image = null;
    var part_image = null;

    if (data.manufacturer_detail) {
        manufacturer_image = data.manufacturer_detail.image;
    }

    if (data.part_detail) {
        part_image = data.part_detail.thumbnail || data.part_detail.image;
    }

    var html = '';

    html += select2Thumbnail(manufacturer_image);
    html += select2Thumbnail(part_image);

    html += ` <span><b>${data.manufacturer_detail.name}</b> - ${data.MPN}</span>`;
    html += ` - <i>${data.part_detail.full_name}</i>`;

    html += `<span class='float-right'><small>{% trans "Manufacturer Part ID" %}: ${data.pk}</small></span>`;

    return html;
}

// Renderer for "SupplierPart" model
// eslint-disable-next-line no-unused-vars
function renderSupplierPart(name, data, parameters={}, options={}) {

    var supplier_image = null;
    var part_image = null;

    if (data.supplier_detail) {
        supplier_image = data.supplier_detail.image;
    }

    if (data.part_detail) {
        part_image = data.part_detail.thumbnail || data.part_detail.image;
    }

    var html = '';

    html += select2Thumbnail(supplier_image);
    html += select2Thumbnail(part_image);

    html += ` <span><b>${data.supplier_detail.name}</b> - ${data.SKU}</span>`;
    html += ` - <i>${data.part_detail.full_name}</i>`;

    html += `<span class='float-right'><small>{% trans "Supplier Part ID" %}: ${data.pk}</small></span>`;

    return html;

}
<commit_msg>Hide ID values by default in model_renderers.js<commit_after>{% load i18n %}

/* globals
    blankImage,
    select2Thumbnail
*/

/* exported
    renderBuild,
    renderCompany,
    renderManufacturerPart,
    renderOwner,
    renderPart,
    renderPartCategory,
    renderStockItem,
    renderStockLocation,
    renderSupplierPart,
*/

/*
 * This file contains functions for rendering various InvenTree database models,
 * in particular for displaying them in modal forms in a 'select2' context.
 *
 * Each renderer is provided with three arguments:
 *
 * - name: The 'name' of the model instance in the referring model
 * - data: JSON data which represents the model instance. Returned via a GET request.
 * - parameters: The field parameters provided via an OPTIONS request to the endpoint.
 * - options: User options provided by the client
 */

// Should the ID be rendered for this string
function renderId(title, pk, parameters={}) {

    // Default = do not display
    var render = false;

    if ('render_pk' in parameters) {
        render = parameters['render_pk'];
    }

    if (render) {
        return `<span class='float-right'><small>${title}: ${pk}</small></span>`;
    } else {
        return '';
    }
}

// Renderer for "Company" model
// eslint-disable-next-line no-unused-vars
function renderCompany(name, data, parameters={}, options={}) {

    var html = select2Thumbnail(data.image);

    html += `<span><b>${data.name}</b></span> - <i>${data.description}</i>`;

    html += renderId('{% trans "Company ID" %}', data.pk, parameters);

    return html;
}

// Renderer for "StockItem" model
// eslint-disable-next-line no-unused-vars
function renderStockItem(name, data, parameters={}, options={}) {

    var image = blankImage();

    if (data.part_detail) {
        image = data.part_detail.thumbnail || data.part_detail.image || blankImage();
    }

    var render_part_detail = true;

    if ('render_part_detail' in parameters) {
        render_part_detail = parameters['render_part_detail'];
    }

    var part_detail = '';

    if (render_part_detail) {
        part_detail = `<img src='${image}' class='select2-thumbnail'><span>${data.part_detail.full_name}</span> - `;
    }

    var render_location_detail = false;

    if ('render_location_detail' in parameters) {
        render_location_detail = parameters['render_location_detail'];
    }

    var location_detail = '';

    if (render_location_detail && data.location_detail) {
        location_detail = ` <small>- (<em>${data.location_detail.name}</em>)</small>`;
    }

    var stock_detail = '';

    if (data.serial && data.quantity == 1) {
        stock_detail = `{% trans "Serial Number" %}: ${data.serial}`;
    } else if (data.quantity == 0) {
        stock_detail = `<span class='badge rounded-pill bg-danger'>{% trans "No Stock"% }</span>`;
    } else {
        stock_detail = `{% trans "Quantity" %}: ${data.quantity}`;
    }

    var html = `

        ${part_detail}
        ${stock_detail}
        ${location_detail}
        ${renderId('{% trans "Stock ID" %}', data.pk, parameters)}

    `;

    return html;
}

// Renderer for "StockLocation" model
// eslint-disable-next-line no-unused-vars
function renderStockLocation(name, data, parameters={}, options={}) {

    var level = '- '.repeat(data.level);

    var html = `<span>${level}${data.pathstring}</span>`;

    var render_description = true;

    if ('render_description' in parameters) {
        render_description = parameters['render_description'];
    }

    if (render_description && data.description) {
        html += ` - <i>${data.description}</i>`;
    }

    html += `<span class='float-right'><small>{% trans "Location ID" %}: ${data.pk}</small></span>`;

    return html;
}

// eslint-disable-next-line no-unused-vars
function renderBuild(name, data, parameters={}, options={}) {

    var image = null;

    if (data.part_detail && data.part_detail.thumbnail) {
        image = data.part_detail.thumbnail;
    }

    var html = select2Thumbnail(image);

    html += `<span><b>${data.reference}</b></span> - ${data.quantity} x ${data.part_detail.full_name}`;
    html += `<span class='float-right'><small>{% trans "Build ID" %}: ${data.pk}</span></span>`;

    html += `<p><i>${data.title}</i></p>`;

    return html;
}

// Renderer for "Part" model
// eslint-disable-next-line no-unused-vars
function renderPart(name, data, parameters={}, options={}) {

    var html = select2Thumbnail(data.image);

    html += ` <span>${data.full_name || data.name}</span>`;

    if (data.description) {
        html += ` - <i><small>${data.description}</small></i>`;
    }

    var stock_data = '';

    if (user_settings.PART_SHOW_QUANTITY_IN_FORMS) {
        stock_data = partStockLabel(data);
    }

    var extra = '';

    if (!data.active) {
        extra += `<span class='badge badge-right rounded-pill bg-danger'>{% trans "Inactive" %}</span>`;
    }

    html += `

            ${stock_data}
            ${extra}
            ${renderId('{% trans "Part ID" %}', data.pk, parameters)}

    `;

    return html;
}

// Renderer for "User" model
// eslint-disable-next-line no-unused-vars
function renderUser(name, data, parameters={}, options={}) {

    var html = `<span>${data.username}</span>`;

    if (data.first_name && data.last_name) {
        html += ` - <i>${data.first_name} ${data.last_name}</i>`;
    }

    return html;
}

// Renderer for "Owner" model
// eslint-disable-next-line no-unused-vars
function renderOwner(name, data, parameters={}, options={}) {

    var html = `<span>${data.name}</span>`;

    switch (data.label) {
    case 'user':
        html += `<span class='float-right fas fa-user'></span>`;
        break;
    case 'group':
        html += `<span class='float-right fas fa-users'></span>`;
        break;
    default:
        break;
    }

    return html;
}

// Renderer for "PurchaseOrder" model
// eslint-disable-next-line no-unused-vars
function renderPurchaseOrder(name, data, parameters={}, options={}) {

    var prefix = global_settings.PURCHASEORDER_REFERENCE_PREFIX;
    var html = `<span>${prefix}${data.reference}</span>`;

    var thumbnail = null;

    if (data.supplier_detail) {
        thumbnail = data.supplier_detail.thumbnail || data.supplier_detail.image;

        html += ' - ' + select2Thumbnail(thumbnail);
        html += `<span>${data.supplier_detail.name}</span>`;
    }

    if (data.description) {
        html += ` - <em>${data.description}</em>`;
    }

    html += renderId('{% trans "Order ID" %}', data.pk, parameters);

    return html;
}

// Renderer for "SalesOrder" model
// eslint-disable-next-line no-unused-vars
function renderSalesOrder(name, data, parameters={}, options={}) {

    var prefix = global_settings.SALESORDER_REFERENCE_PREFIX;
    var html = `<span>${prefix}${data.reference}</span>`;

    var thumbnail = null;

    if (data.customer_detail) {
        thumbnail = data.customer_detail.thumbnail || data.customer_detail.image;

        html += ' - ' + select2Thumbnail(thumbnail);
        html += `<span>${data.customer_detail.name}</span>`;
    }

    if (data.description) {
        html += ` - <em>${data.description}</em>`;
    }

    html += renderId('{% trans "Order ID" %}', data.pk, parameters);

    return html;
}

// Renderer for "SalesOrderShipment" model
// eslint-disable-next-line no-unused-vars
function renderSalesOrderShipment(name, data, parameters={}, options={}) {

    var so_prefix = global_settings.SALESORDER_REFERENCE_PREFIX;

    var html = `
    ${so_prefix}${data.order_detail.reference} - {% trans "Shipment" %} ${data.reference}

        {% trans "Shipment ID" %}: ${data.pk}

    `;

    return html;
}

// Renderer for "PartCategory" model
// eslint-disable-next-line no-unused-vars
function renderPartCategory(name, data, parameters={}, options={}) {

    var level = '- '.repeat(data.level);

    var html = `<span>${level}${data.pathstring}</span>`;

    if (data.description) {
        html += ` - <i>${data.description}</i>`;
    }

    html += `<span class='float-right'><small>{% trans "Category ID" %}: ${data.pk}</small></span>`;

    return html;
}

// eslint-disable-next-line no-unused-vars
function renderPartParameterTemplate(name, data, parameters={}, options={}) {

    var units = '';

    if (data.units) {
        units = ` [${data.units}]`;
    }

    var html = `<span>${data.name}${units}</span>`;

    return html;
}

// Renderer for "ManufacturerPart" model
// eslint-disable-next-line no-unused-vars
function renderManufacturerPart(name, data, parameters={}, options={}) {

    var manufacturer_image = null;
    var part_image = null;

    if (data.manufacturer_detail) {
        manufacturer_image = data.manufacturer_detail.image;
    }

    if (data.part_detail) {
        part_image = data.part_detail.thumbnail || data.part_detail.image;
    }

    var html = '';

    html += select2Thumbnail(manufacturer_image);
    html += select2Thumbnail(part_image);

    html += ` <span><b>${data.manufacturer_detail.name}</b> - ${data.MPN}</span>`;
    html += ` - <i>${data.part_detail.full_name}</i>`;

    html += `<span class='float-right'><small>{% trans "Manufacturer Part ID" %}: ${data.pk}</small></span>`;

    return html;
}

// Renderer for "SupplierPart" model
// eslint-disable-next-line no-unused-vars
function renderSupplierPart(name, data, parameters={}, options={}) {

    var supplier_image = null;
    var part_image = null;

    if (data.supplier_detail) {
        supplier_image = data.supplier_detail.image;
    }

    if (data.part_detail) {
        part_image = data.part_detail.thumbnail || data.part_detail.image;
    }

    var html = '';

    html += select2Thumbnail(supplier_image);
    html += select2Thumbnail(part_image);

    html += ` <span><b>${data.supplier_detail.name}</b> - ${data.SKU}</span>`;
    html += ` - <i>${data.part_detail.full_name}</i>`;

    html += `<span class='float-right'><small>{% trans "Supplier Part ID" %}: ${data.pk}</small></span>`;

    return html;

}
<|endoftext|>


<commit_before>django.jQuery(function($) {
	'use strict';
	var $link_type = $("#id_link_type"), $cmspage_select = $("#id_cms_page");
	var $link_target = $(".glossary-widget .glossary_target");

	django.cascade.LinkPluginBase = ring.create({
		LinkType: ring.create({
			constructor: function(selector, addTarget) {
				this.$element = $(selector);
				this.addTarget = Boolean(addTarget);
			},
			show: function() {
				this.$element.show();
				if (this.addTarget) {
					$link_target.show();
				}
			},
			hide: function() {
				this.$element.hide();
			}
		}),
		constructor: function() {
			var self = this;
			this.linkTypes = {};
			this.$super();
			this.initializeLinkTypes();

			// register event handlers on changing link_type and cms_page select boxes
			$link_type.change(function(evt) {
				self.toggleLinkTypes(evt.target.value);
			});
			$cmspage_select.change(function(evt) {
				self.toggleCMSPage(evt.target.value);
			});
			this.refreshChangeForm();
		},
		initializeLinkTypes: function() {
			this.linkTypes['cmspage'] = new this.LinkType('.form-row .field-box.field-cms_page, .form-row .field-box.field-section', true);
			this.linkTypes['download'] = new this.LinkType('.form-row .field-box.field-download_file');
			this.linkTypes['exturl'] = new this.LinkType('.form-row .field-box.field-ext_url', true);
			this.linkTypes['email'] = new this.LinkType('.form-row .field-box.field-mail_to');
		},
		toggleLinkTypes: function(linkTypeName) {
			$.each(this.linkTypes, function(name, linkType) {
				if (name === linkTypeName) {
					linkType.show();
				} else {
					linkType.hide();
				}
			});
			if (!this.linkTypes[linkTypeName] || !this.linkTypes[linkTypeName].addTarget) {
				$link_target.hide();
			}
		},
		toggleSharedGlossary: function($option) {
			var glossary = $option.data('glossary');
			try {
				$link_type.val(glossary['link']['type']);
				try {
					$cmspage_select.select2("data", {id: glossary['link']['pk'], text: glossary['link']['identifier']});
					$cmspage_select.prop('disabled', true);
				} catch(err) {
					if (!(err instanceof TypeError))
						throw err;
					$cmspage_select.val(glossary['link']['pk']);
				}
				$('#id_ext_url').val(glossary['link']['url']);
				$('#id_mail_to').val(glossary['link']['email']);
			} catch (err) {
				try {
					if (!(err instanceof TypeError))
						throw err;
					$cmspage_select.prop('disabled', false);
				} catch (err) {
					if (!(err instanceof TypeError))
						console.error(err);
				}
			}
			if (this.$super) {
				this.$super($option);
			} else {
				this.refreshChangeForm();
			}
		},
		toggleCMSPage: function(page_id) {
			var url = django.cascade.page_sections_url + page_id, $selSection = $('#id_section');

			$.get(url, function(response) {
				var k, val;

				$selSection.children('option:gt(0)').remove();
				for (k = 0; k < response.element_ids.length; k++) {
					val = response.element_ids[k];
					$selSection.append($("<option></option>").attr("value", val[0]).text(val[1]));
				}
				$selSection.val(null);
			});
		},
		refreshChangeForm: function() {
			this.toggleLinkTypes($link_type.val());
			this.$super && this.$super();
		}
	});
});
<commit_msg>Fix: Input field for link title is hidden if “no link” selected<commit_after>django.jQuery(function($) {
	'use strict';
	var $link_type = $("#id_link_type"), $cmspage_select = $("#id_cms_page");
	var $link_target = $(".glossary-widget .glossary_target");
	var $link_title = $(".glossary-widget .glossary_title");

	django.cascade.LinkPluginBase = ring.create({
		LinkType: ring.create({
			constructor: function(selector, addTarget) {
				this.$element = $(selector);
				this.addTarget = Boolean(addTarget);
			},
			show: function() {
				this.$element.show();
				if (this.addTarget) {
					$link_target.show();
				}
				$link_title.show();
			},
			hide: function() {
				this.$element.hide();
			}
		}),
		constructor: function() {
			var self = this;
			this.linkTypes = {};
			this.$super();
			this.initializeLinkTypes();

			// register event handlers on changing link_type and cms_page select boxes
			$link_type.change(function(evt) {
				self.toggleLinkTypes(evt.target.value);
			});
			$cmspage_select.change(function(evt) {
				self.toggleCMSPage(evt.target.value);
			});
			this.refreshChangeForm();
		},
		initializeLinkTypes: function() {
			this.linkTypes['cmspage'] = new this.LinkType('.form-row .field-box.field-cms_page, .form-row .field-box.field-section', true);
			this.linkTypes['download'] = new this.LinkType('.form-row .field-box.field-download_file');
			this.linkTypes['exturl'] = new this.LinkType('.form-row .field-box.field-ext_url', true);
			this.linkTypes['email'] = new this.LinkType('.form-row .field-box.field-mail_to');
		},
		toggleLinkTypes: function(linkTypeName) {
			$.each(this.linkTypes, function(name, linkType) {
				if (name === linkTypeName) {
					linkType.show();
				} else {
					linkType.hide();
				}
			});
			if (!this.linkTypes[linkTypeName] || !this.linkTypes[linkTypeName].addTarget) {
				$link_target.hide();
			}
			if (!this.linkTypes[linkTypeName]) {
				$link_title.hide();
			}
		},
		toggleSharedGlossary: function($option) {
			var glossary = $option.data('glossary');
			try {
				$link_type.val(glossary['link']['type']);
				try {
					$cmspage_select.select2("data", {id: glossary['link']['pk'], text: glossary['link']['identifier']});
					$cmspage_select.prop('disabled', true);
				} catch(err) {
					if (!(err instanceof TypeError))
						throw err;
					$cmspage_select.val(glossary['link']['pk']);
				}
				$('#id_ext_url').val(glossary['link']['url']);
				$('#id_mail_to').val(glossary['link']['email']);
			} catch (err) {
				try {
					if (!(err instanceof TypeError))
						throw err;
					$cmspage_select.prop('disabled', false);
				} catch (err) {
					if (!(err instanceof TypeError))
						console.error(err);
				}
			}
			if (this.$super) {
				this.$super($option);
			} else {
				this.refreshChangeForm();
			}
		},
		toggleCMSPage: function(page_id) {
			var url = django.cascade.page_sections_url + page_id, $selSection = $('#id_section');

			$.get(url, function(response) {
				var k, val;

				$selSection.children('option:gt(0)').remove();
				for (k = 0; k < response.element_ids.length; k++) {
					val = response.element_ids[k];
					$selSection.append($("<option></option>").attr("value", val[0]).text(val[1]));
				}
				$selSection.val(null);
			});
		},
		refreshChangeForm: function() {
			this.toggleLinkTypes($link_type.val());
			this.$super && this.$super();
		}
	});
});
<|endoftext|>


<commit_before>Travis.Controllers.Application = Backbone.Controller.extend({
  routes: {
    '':                                          'recent',
    // '!/:owner':               'byOwner',
    // FIXME: I would suggest to use !/repositories/:owner/:name, to make it more rest-like.
    // Because, for instance, now we should put myRepositories on top so that it could get matched. Unambigous routes rule!
    '!/:owner/:name/L:line_number':              'repository',
    '!/:owner/:name':                            'repository',
    '!/:owner/:name/builds':                     'repositoryHistory',
    '!/:owner/:name/builds/:id/L:line_number':   'repositoryBuild',
    '!/:owner/:name/builds/:id':                 'repositoryBuild',
  },
  initialize: function() {
    _.bindAll(this, 'recent', 'byUser', 'repository', 'repositoryHistory', 'repositoryBuild', 'repositoryShow', 'repositorySelected',
              'buildQueued', 'buildStarted', 'buildLogged', 'buildFinished', 'buildRemoved');
  },

  run: function() {
    this.repositories = new Travis.Collections.Repositories();
    this.builds       = new Travis.Collections.AllBuilds();
    this.jobs         = new Travis.Collections.Jobs([], { queue: 'builds' });
    this.jobsRails    = new Travis.Collections.Jobs([], { queue: 'rails' });
    this.workers      = new Travis.Collections.Workers();

    this.repositoriesList = new Travis.Views.Repositories.List();
    this.repositoryShow   = new Travis.Views.Repository.Show({ parent: this });
    this.workersView      = new Travis.Views.Workers.List();
    this.jobsView         = new Travis.Views.Jobs.List({ queue: 'builds' });
    this.jobsRailsView    = new Travis.Views.Jobs.List({ queue: 'rails' });

    $('#left #tab_recent .tab').append(this.repositoriesList.render().el);
    $('#main').append(this.repositoryShow.render().el);

    this.repositoriesList.attachTo(this.repositories);
    this.repositoryShow.attachTo(this.repositories)
    this.workersView.attachTo(this.workers)
    this.jobsView.attachTo(this.jobs)
    this.jobsRailsView.attachTo(this.jobsRails)
    this.repositories.bind('select', this.repositorySelected);

    this.bind('build:started',    this.buildStarted);
    this.bind('build:finished',   this.buildFinished);
    this.bind('build:configured', this.buildConfigured);
    this.bind('build:log',        this.buildLogged);
    this.bind('build:queued',     this.buildQueued);
    this.bind('build:removed',    this.buildRemoved); /* UNTESTED */

    this.workers.fetch();
    this.jobs.fetch();
    this.jobsRails.fetch();
  },

  // actions

  recent: function() {
    this.reset();
    this.followBuilds = true;
    this.selectTab('current');
    this.repositories.whenFetched(_.bind(function () {
      this.repositories.selectLast();

    }, this));
  },
  repository: function(owner, name, line_number) {
    console.log ("application#repository: ", arguments)
    this.reset();
    this.trackPage();
    this.startLoading();
    window.params = { owner: owner, name: name, line_number: line_number, action: 'repository' }
    this.selectTab('current');
    this.repositories.whenFetched(_.bind(function(repositories) {
      repositories.selectLastBy({ slug: owner + '/' + name });

    }, this));
  },
  repositoryHistory: function(owner, name) {
    console.log ("application#repositoryHistory: ", arguments)
    this.reset();
    this.trackPage();
    this.selectTab('history');
    this.repositories.whenFetched(_.bind(function(repositories) {
      repositories.selectLastBy({ slug: owner + '/' + name })

    }, this));
  },
  repositoryBuild: function(owner, name, buildId, line_number) {
    console.log ("application#repositoryBuild: ", arguments)
    this.reset();
    this.trackPage();
    this.startLoading();
    window.params = { owner: owner, name: name, build_id: buildId, line_number: line_number, action: 'repositoryBuild' }
    this.buildId = parseInt(buildId);
    this.selectTab('build');
    this.repositories.whenFetched(_.bind(function(repositories) {
      repositories.selectLastBy({ slug: owner + '/' + name })

    }, this));
  },

  // helpers
  reset: function() {
    delete this.buildId;
    delete this.tab;
    this.followBuilds = false;
    window.params = {};
  },
  startLoading: function() {
    $('#main').addClass('loading')
  },
  stopLoading: function() {
    $('#main').removeClass('loading')
  },
  trackPage: function() {
    window._gaq = _gaq || [];
    window._gaq.push(['_trackPageview']);
  },

  // internal events
  repositorySelected: function(repository) {
    repository.builds.bind('finish_get_or_fetch', function() { this.stopLoading() }.bind(this))

    switch(this.tab) {
      case 'current':
        repository.builds.select(repository.get('last_build_id'));
        break;
      case 'build':
        repository.builds.select(this.buildId);
        break;
      case 'history':
        if(!repository.builds.fetched) repository.builds.fetch();
        break;
    };
  },

  // external events

  buildQueued: function(data) {
    this.addJob(data);
  },
  buildStarted: function(data) {
    this.removeJob(data);
    this.repositories.update(data);

    if((this.followBuilds || this.tab == 'current' && this.repositories.selected().get('slug') == data.slug) && !this.buildId && !data.build.parent_id) {
      var repository = this.repositories.get(data.id);
      if(!repository.selected) repository.select();
      repository.builds.select(data.build.id);
    }
  },
  buildConfigured: function(data) {
    this.removeJob(data);
    this.repositories.update(data);
  },
  buildFinished: function(data) {
    this.repositories.update(data);
  },
  buildRemoved: function(data) {
    this.removeJob(data);
  },
  buildLogged: function(data) {
    this.repositories.update(data);
  },
  selectTab: function(tab) {
    this.tab = tab;
    this.repositoryShow.activateTab(this.tab);
  },
  addJob: function(data) {
    this.jobsCollection(data).add({ number: data.build.number, id: data.build.id, repository: { slug: data.slug } });
  },
  removeJob: function(data) {
    this.jobsCollection(data).remove({ id: data.build.id });
  },
  jobsCollection: function(data) {
    return this.buildingRails(data) ? this.jobsRails : this.jobs;
  },
  buildingRails: function (data) {
    return data.slug && data.slug == 'rails/rails';
  }
});

<commit_msg>Adding debug log messages to application.js<commit_after>Travis.Controllers.Application = Backbone.Controller.extend({
  routes: {
    '':                                          'recent',
    // '!/:owner':               'byOwner',
    // FIXME: I would suggest to use !/repositories/:owner/:name, to make it more rest-like.
    // Because, for instance, now we should put myRepositories on top so that it could get matched. Unambigous routes rule!
    '!/:owner/:name/L:line_number':              'repository',
    '!/:owner/:name':                            'repository',
    '!/:owner/:name/builds':                     'repositoryHistory',
    '!/:owner/:name/builds/:id/L:line_number':   'repositoryBuild',
    '!/:owner/:name/builds/:id':                 'repositoryBuild',
  },
  initialize: function() {
    _.bindAll(this, 'recent', 'byUser', 'repository', 'repositoryHistory', 'repositoryBuild', 'repositoryShow', 'repositorySelected',
              'buildQueued', 'buildStarted', 'buildLogged', 'buildFinished', 'buildRemoved');
  },

  run: function() {
    this.repositories = new Travis.Collections.Repositories();
    this.builds       = new Travis.Collections.AllBuilds();
    this.jobs         = new Travis.Collections.Jobs([], { queue: 'builds' });
    this.jobsRails    = new Travis.Collections.Jobs([], { queue: 'rails' });
    this.workers      = new Travis.Collections.Workers();

    this.repositoriesList = new Travis.Views.Repositories.List();
    this.repositoryShow   = new Travis.Views.Repository.Show({ parent: this });
    this.workersView      = new Travis.Views.Workers.List();
    this.jobsView         = new Travis.Views.Jobs.List({ queue: 'builds' });
    this.jobsRailsView    = new Travis.Views.Jobs.List({ queue: 'rails' });

    $('#left #tab_recent .tab').append(this.repositoriesList.render().el);
    $('#main').append(this.repositoryShow.render().el);

    this.repositoriesList.attachTo(this.repositories);
    this.repositoryShow.attachTo(this.repositories)
    this.workersView.attachTo(this.workers)
    this.jobsView.attachTo(this.jobs)
    this.jobsRailsView.attachTo(this.jobsRails)
    this.repositories.bind('select', this.repositorySelected);

    this.bind('build:started',    this.buildStarted);
    this.bind('build:finished',   this.buildFinished);
    this.bind('build:configured', this.buildConfigured);
    this.bind('build:log',        this.buildLogged);
    this.bind('build:queued',     this.buildQueued);
    this.bind('build:removed',    this.buildRemoved); /* UNTESTED */

    this.workers.fetch();
    this.jobs.fetch();
    this.jobsRails.fetch();
  },

  // actions

  recent: function() {
    this.reset();
    this.followBuilds = true;
    this.selectTab('current');
    this.repositories.whenFetched(_.bind(function () {
      this.repositories.selectLast();

    }, this));
  },
  repository: function(owner, name, line_number) {
    console.log ("application#repository: ", arguments)
    this.reset();
    this.trackPage();
    this.startLoading();
    window.params = { owner: owner, name: name, line_number: line_number, action: 'repository' }
    this.selectTab('current');
    this.repositories.whenFetched(_.bind(function(repositories) {
      repositories.selectLastBy({ slug: owner + '/' + name });

    }, this));
  },
  repositoryHistory: function(owner, name) {
    console.log ("application#repositoryHistory: ", arguments)
    this.reset();
    this.trackPage();
    this.selectTab('history');
    this.repositories.whenFetched(_.bind(function(repositories) {
      repositories.selectLastBy({ slug: owner + '/' + name })

    }, this));
  },
  repositoryBuild: function(owner, name, buildId, line_number) {
    console.log ("application#repositoryBuild: ", arguments)
    this.reset();
    this.trackPage();
    this.startLoading();
    window.params = { owner: owner, name: name, build_id: buildId, line_number: line_number, action: 'repositoryBuild' }
    this.buildId = parseInt(buildId);
    this.selectTab('build');
    this.repositories.whenFetched(_.bind(function(repositories) {
      repositories.selectLastBy({ slug: owner + '/' + name })

    }, this));
  },

  // helpers
  reset: function() {
    delete this.buildId;
    delete this.tab;
    this.followBuilds = false;
    window.params = {};
  },
  startLoading: function() {
    $('#main').addClass('loading')
  },
  stopLoading: function() {
    $('#main').removeClass('loading')
  },
  trackPage: function() {
    window._gaq = _gaq || [];
    window._gaq.push(['_trackPageview']);
  },

  // internal events
  repositorySelected: function(repository) {
    repository.builds.bind('finish_get_or_fetch', function() { this.stopLoading() }.bind(this))

    switch(this.tab) {
      case 'current':
        repository.builds.select(repository.get('last_build_id'));
        break;
      case 'build':
        repository.builds.select(this.buildId);
        break;
      case 'history':
        if(!repository.builds.fetched) repository.builds.fetch();
        break;
    };
  },

  // external events

  buildQueued: function(data) {
    console.log ("application#buildQueued: ", arguments)
    this.addJob(data);
  },
  buildStarted: function(data) {
    console.log ("application#buildStarted: ", arguments)
    this.removeJob(data);
    this.repositories.update(data);

    if((this.followBuilds || this.tab == 'current' && this.repositories.selected().get('slug') == data.slug) && !this.buildId && !data.build.parent_id) {
      var repository = this.repositories.get(data.id);
      if(!repository.selected) repository.select();
      repository.builds.select(data.build.id);
    }
  },
  buildConfigured: function(data) {
    console.log ("application#buildConfigured: ", arguments)
    this.removeJob(data);
    this.repositories.update(data);
  },
  buildFinished: function(data) {
    console.log ("application#buildFinished: ", arguments)
    this.repositories.update(data);
  },
  buildRemoved: function(data) {
    console.log ("application#buildRemoved: ", arguments)
    this.removeJob(data);
  },
  buildLogged: function(data) {
    console.log ("application#buildLogged: ", arguments)
    this.repositories.update(data);
  },

  selectTab: function(tab) {
    this.tab = tab;
    this.repositoryShow.activateTab(this.tab);
  },
  addJob: function(data) {
    this.jobsCollection(data).add({ number: data.build.number, id: data.build.id, repository: { slug: data.slug } });
  },
  removeJob: function(data) {
    this.jobsCollection(data).remove({ id: data.build.id });
  },
  jobsCollection: function(data) {
    return this.buildingRails(data) ? this.jobsRails : this.jobs;
  },
  buildingRails: function (data) {
    return data.slug && data.slug == 'rails/rails';
  }
});

<|endoftext|>


<commit_before>angular.module('dashboard.directives.ModelFieldReference', [
  "dashboard.Config",
  "dashboard.services.GeneralModel",
  "ui.select"
])

.directive('modelFieldReferenceView', function($compile) {
  return {
    restrict: 'E',
    template: '{{ options.model }}: {{ data[options.key] }}',
    scope: {
      options: '=options',
      data: '=ngModel',
      required: 'ngRequired',
      disabled: 'ngDisabled'
    },
    link: function(scope, element, attrs) {
    }
  };
})

.directive('modelFieldReferenceEdit', function($compile, $cookies, Config, GeneralModelService) {
  function getTemplate(multiple, matchTemplate, choiceTemplate) {
    var template = '';
    if (multiple) {
      //multi-select
      template = '\
      <ui-select multiple ng-model="selected.items" on-select="onSelect($item, $model)" on-remove="onRemove($item, $model)"  ng-disabled="disabled"> \
      <ui-select-match placeholder="{{ options.placeholder }}">'+ matchTemplate +'</ui-select-match> \
      <ui-select-choices repeat="item in list" refresh="refreshChoices($select.search)" refresh-delay="200">' + choiceTemplate + '</ui-select-choices> \
      </ui-select>';
    } else {
      //single-select
      template = '\
      <ui-select ng-model="selected.item" on-select="onSelect($item, $model)" ng-required="ngRequired" ng-disabled="disabled" > \
      <ui-select-match placeholder="{{ options.placeholder }}">'+ matchTemplate +'</ui-select-match> \
      <ui-select-choices repeat="item in list" refresh="refreshChoices($select.search)" refresh-delay="200">' + choiceTemplate + '</ui-select-choices> \
      </ui-select>';
    }
    return template;
  }
  return {
    restrict: 'E',
    scope: {
      key: '=key',
      property: '=property',
      options: '=options',
      data: '=ngModel',
      modelData: '=modelData',
      disabled: '=ngDisabled',
      rowData: "=ngRowData", //for use in the model list edit mode
      textOutputPath: '=ngTextOutputPath', //output the selected text to this path in the rowData
      onModelChanged: "&onModelChanged"
    },
    link: function(scope, element, attrs) {

        scope.moment = moment;
        scope.isFirstTimeLoad = true;
        scope.selected= {};
        scope.selected.items = []; //for multi-select
        scope.selected.item = null; //for single select; initialize to null so placeholder is displayed
        scope.list = [];

        function replaceSessionVariables(string) {
          if (typeof string !== 'string') return string;
          try {
            //Look for session variables in string
            var session = JSON.parse($cookies.session); //needed for eval() below
            var searchString = "{session.";
            var startPos = string.indexOf(searchString);
            while (startPos > -1) {
              var endPos = string.indexOf("}", startPos);
              if (endPos == -1) {
                console.error("ModelList session parsing malformed for string");
                break;
              }
              var sessionKey = string.substring(startPos+1, endPos);
              string = string.slice(0, startPos) + eval(sessionKey) + string.slice(endPos+1);
              startPos = string.indexOf(searchString);
            }
            //Look for model data variable strings
            searchString = "{";
            startPos = string.indexOf(searchString);
            while (startPos > -1) {
              var endPos = string.indexOf("}", startPos);
              if (endPos == -1) {
                console.error("ModelList session parsing malformed for string");
                break;
              }
              var key = string.substring(startPos+1, endPos);
              string = string.slice(0, startPos) + scope.modelData[key] + string.slice(endPos+1);
              startPos = string.indexOf(searchString);
            }
          } catch(e) {
            console.error(e);
          }
          return string;
        }

        scope.refreshChoices = function(search) {
//          console.log(search);
//          console.log("scope.data = " + JSON.stringify(scope.data, null, '  '));
//          console.log("scope.field = " + JSON.stringify(scope.field, null, '  '));
          var model = Config.serverParams.models[scope.options.model];
          var params = { 'filter[limit]': 100 }; //limit only 100 items in drop down list
          params['filter[where]['+scope.options.searchField+'][like]'] = "%" + search + "%";
          if (scope.options.where) {
            //Add additional filtering on reference results
            var keys = Object.keys(scope.options.where);
            for (var i in keys) {
              var key = keys[i];
              params['filter[where][' + key + ']'] = replaceSessionVariables(scope.options.where[key]);
            }
          }
          if (scope.options.filters) {
            var keys = Object.keys(scope.options.filters);
            for (var i in keys) {
              var key = keys[i];
              params[key] = replaceSessionVariables(scope.options.filters[key]);
            }
          }
          var apiPath = model.plural;
          if (scope.options.api) apiPath = replaceSessionVariables(scope.options.api);
          GeneralModelService.list(apiPath, params).then(function(response) {
            if (!response) return; //in case http request was cancelled by newer request
            scope.list = response;
            if (scope.options.allowInsert) {
              var addNewItem = {};
              addNewItem[scope.options.searchField] = "[Add New Item]";
              scope.list.push(addNewItem);
            }
            if (scope.options.allowClear) {
              var addNewItem = {};
              addNewItem[scope.options.searchField] = "[clear]";
              scope.list.unshift(addNewItem);

            }
            if (typeof scope.options.defaultIndex === 'number') {
              if (response[scope.options.defaultIndex]) {
                //scope.selected.items = [response[scope.options.defaultIndex]];
                scope.onSelect(response[scope.options.defaultIndex]);
              }
            }
          });
        };

        var unwatch = scope.$watchCollection('[data, options, modelData]', function(results) {
          if (scope.modelData && scope.modelData && scope.options && scope.options.multiple) {
            if (!scope.property.display.sourceModel) {
              unwatch();
              //No sourceModel so try to populate from modelData for items already selected
              if (scope.modelData[scope.property.display.options.relationship]) {
                scope.selected.items = scope.modelData[scope.property.display.options.relationship];
                scope.list = scope.selected.items; //make sure list contains item otherwise won't be displayed
              }
              return;
            }
            //Lookup multiple records that were previously selected
            var sourceModel = Config.serverParams.models[scope.property.display.sourceModel];
            var referenceModel = Config.serverParams.models[scope.options.model];
            var sourceModelName = sourceModel.plural;
            var referenceModelName = referenceModel.plural;
            var sourceId = scope.modelData[scope.property.display.sourceKey];
            if (!sourceId) {
              return;
            }
            unwatch(); //due to late binding need to unwatch here
            GeneralModelService.getMany(sourceModelName, sourceId, scope.options.relationship)
            .then(function(response) {
              if (!response) return;  //in case http request was cancelled
              if (scope.options.api && response.length > 0) {
                //If custom API is provided then use it
                var params = {filter: { where: {}}};
                params.filter.where[scope.options.key] = {inq: []};
                for (var i in response) {
                  var item = response[i];
                  params.filter.where[scope.options.key].inq.push(item[scope.options.key]);
                }
                apiPath = replaceSessionVariables(scope.options.api);
                GeneralModelService.list(apiPath, params).then(function(response) {
                  if (!response) return;  //in case http request was cancelled
                  scope.selected.items = response;
                  scope.list = response;
                });
              } else {
                scope.selected.items = response;
                scope.list = response;
              }
            });

          } else if (scope.data && scope.options && scope.options.model) {
            //Lookup default reference record
            var model = Config.serverParams.models[scope.options.model];
            //unwatch(); //due to late binding need to unwatch here
            GeneralModelService.get(model.plural, scope.data)
            .then(function(response) {
              if (!response) return;  //in case http request was cancelled
              //console.log("default select = " + JSON.stringify(response));
              scope.selected.item = response;
              scope.list = [scope.selected.item]; //make sure list contains item otherwise won't be displayed
              if (scope.onModelChanged) scope.onModelChanged({'$item': scope.selected.item});
            }, function(error) {
                if (scope.options.allowInsert) {
                  //Not found so just add the item
                  var newItem = {};
                  newItem[scope.options.key] = scope.data;
                  newItem[scope.options.searchField] = scope.data;
                  scope.selected.item = newItem;
                  scope.list.push(newItem);
                }

            });
          }
       });

       scope.onSelect = function(item, model) {
         if (scope.options.multiple) {
           //For multi-select add as relationship array objects to modelData (when saving, the CMS relational-upsert.js will handle it)
           scope.selected.items.push(item);
           //Make sure to loop through all items for junctionMeta (previously loaded items will not have junctionMeta populated)
           if (scope.options.junctionMeta) {
             for (var i in scope.selected.items) {
               var item = scope.selected.items[i];
               //meta data for junction table in a many-to-many situation
               item.junctionMeta = scope.options.junctionMeta;
             }
           }
           //Assign to model data
           scope.modelData[scope.options.relationship] = scope.selected.items;
         } else {
           //For single record reference just assign the ID back to data
           scope.data = item[scope.options.key];
           //emit an event when an item is selected
           scope.$emit('onModelFieldReferenceSelect', scope.modelData, scope.key, item);
           var textValue = item[scope.options.searchField];
            if (item && item[scope.options.searchField] == "[Add New Item]") {
              //console.log("should add " + $select.search);
              var value = element.find("input.ui-select-search").val();
              scope.data = value;
              var newItem = {};
              newItem[scope.options.key] = value;
              newItem[scope.options.searchField] = value;
              scope.selected.item = newItem;
              scope.list.push(newItem);
            } else if (item && item[scope.options.searchField] == "[clear]") {
              //console.log("should add " + $select.search);
              scope.data = null;
              textValue = "";
            }

            //For the Model List Edit View we need a way to return back the
            //text value to be displayed. The config.json can specify the rowData
            //and textOutputPath to retrieve the data
            if (scope.rowData && scope.textOutputPath && item[scope.options.searchField]) {
              if (scope.textOutputPath.indexOf(".") > -1) {
                var path = scope.textOutputPath.split(".");
                var obj = scope.rowData;
                for (var i = 0; i < path.length-1; i++) {
                  var property = path[i];
                  if (!obj[property]) obj[property] = {};
                  obj = obj[property];
                }
                obj[path[path.length-1]] = textValue;
              } else {
                scope.rowData[scope.textOutputPath] = textValue;
              }
            }

            setTimeout(function() {
              //Needed in a timeout so the scope.data gets saved
              //before emitting ngGridEventEndCellEdit
              scope.$emit('ngGridEventEndCellEdit');
            }, 1);
         }
       };

       scope.onRemove = function(item, model) {
         if (scope.options.multiple) {
           //Remove item from array
           var index = scope.selected.items.indexOf(item);
           if (index > -1) {
             scope.selected.items.splice(index, 1);
             if (scope.options.junctionMeta) {
             //Make sure to loop through all items for junctionMeta (previously loaded items will not have junctionMeta populated)
               for (var i in scope.selected.items) {
                 var item = scope.selected.items[i];
                 //meta data for junction table in a many-to-many situation
                 item.junctionMeta = scope.options.junctionMeta;
               }
             }
           }
           scope.modelData[scope.options.relationship] = scope.selected.items;
         } else {
           //For single record reference just assign null
           scope.data = null;
         }
       };

       scope.$on('ngGridEventStartCellEdit', function () {
         //When editing focus on the reference button
         element.find("button").trigger("click");
         element.find("input.ui-select-search").focus();
       });

       element.html(getTemplate(scope.options.multiple, scope.options.matchTemplate, scope.options.choiceTemplate)).show();
       $compile(element.contents())(scope);

    }
  };
})

;
<commit_msg>Refresh pull down options on field focus<commit_after>angular.module('dashboard.directives.ModelFieldReference', [
  "dashboard.Config",
  "dashboard.services.GeneralModel",
  "ui.select"
])

.directive('modelFieldReferenceView', function($compile) {
  return {
    restrict: 'E',
    template: '{{ options.model }}: {{ data[options.key] }}',
    scope: {
      options: '=options',
      data: '=ngModel',
      required: 'ngRequired',
      disabled: 'ngDisabled'
    },
    link: function(scope, element, attrs) {
    }
  };
})

.directive('modelFieldReferenceEdit', function($compile, $cookies, Config, GeneralModelService) {
  function getTemplate(multiple, matchTemplate, choiceTemplate) {
    var template = '';
    if (multiple) {
      //multi-select
      template = '\
      <ui-select multiple ng-model="selected.items" on-select="onSelect($item, $model)" on-remove="onRemove($item, $model)"  ng-disabled="disabled"> \
      <ui-select-match placeholder="{{ options.placeholder }}">'+ matchTemplate +'</ui-select-match> \
      <ui-select-choices repeat="item in list" refresh="refreshChoices($select.search)" refresh-delay="200">' + choiceTemplate + '</ui-select-choices> \
      </ui-select>';
    } else {
      //single-select
      template = '\
      <ui-select ng-model="selected.item" on-select="onSelect($item, $model)" ng-required="ngRequired" ng-disabled="disabled" > \
      <ui-select-match ng-focus="refreshChoices($select.search)" placeholder="{{ options.placeholder }}">'+ matchTemplate +'</ui-select-match> \
      <ui-select-choices repeat="item in list" refresh="refreshChoices($select.search)" refresh-delay="200">' + choiceTemplate + '</ui-select-choices> \
      </ui-select>';
    }
    return template;
  }
  return {
    restrict: 'E',
    scope: {
      key: '=key',
      property: '=property',
      options: '=options',
      data: '=ngModel',
      modelData: '=modelData',
      disabled: '=ngDisabled',
      rowData: "=ngRowData", //for use in the model list edit mode
      textOutputPath: '=ngTextOutputPath', //output the selected text to this path in the rowData
      onModelChanged: "&onModelChanged"
    },
    link: function(scope, element, attrs) {

        scope.moment = moment;
        scope.isFirstTimeLoad = true;
        scope.selected= {};
        scope.selected.items = []; //for multi-select
        scope.selected.item = null; //for single select; initialize to null so placeholder is displayed
        scope.list = [];

        function replaceSessionVariables(string) {
          if (typeof string !== 'string') return string;
          try {
            //Look for session variables in string
            var session = JSON.parse($cookies.session); //needed for eval() below
            var searchString = "{session.";
            var startPos = string.indexOf(searchString);
            while (startPos > -1) {
              var endPos = string.indexOf("}", startPos);
              if (endPos == -1) {
                console.error("ModelList session parsing malformed for string");
                break;
              }
              var sessionKey = string.substring(startPos+1, endPos);
              string = string.slice(0, startPos) + eval(sessionKey) + string.slice(endPos+1);
              startPos = string.indexOf(searchString);
            }
            //Look for model data variable strings
            searchString = "{";
            startPos = string.indexOf(searchString);
            while (startPos > -1) {
              var endPos = string.indexOf("}", startPos);
              if (endPos == -1) {
                console.error("ModelList session parsing malformed for string");
                break;
              }
              var key = string.substring(startPos+1, endPos);
              string = string.slice(0, startPos) + scope.modelData[key] + string.slice(endPos+1);
              startPos = string.indexOf(searchString);
            }
          } catch(e) {
            console.error(e);
          }
          return string;
        }

        scope.refreshChoices = function(search) {
//          console.log(search);
//          console.log("scope.data = " + JSON.stringify(scope.data, null, '  '));
//          console.log("scope.field = " + JSON.stringify(scope.field, null, '  '));
          var model = Config.serverParams.models[scope.options.model];
          var params = { 'filter[limit]': 100 }; //limit only 100 items in drop down list
          params['filter[where]['+scope.options.searchField+'][like]'] = "%" + search + "%";
          if (scope.options.where) {
            //Add additional filtering on reference results
            var keys = Object.keys(scope.options.where);
            for (var i in keys) {
              var key = keys[i];
              params['filter[where][' + key + ']'] = replaceSessionVariables(scope.options.where[key]);
            }
          }
          if (scope.options.filters) {
            var keys = Object.keys(scope.options.filters);
            for (var i in keys) {
              var key = keys[i];
              params[key] = replaceSessionVariables(scope.options.filters[key]);
            }
          }
          var apiPath = model.plural;
          if (scope.options.api) apiPath = replaceSessionVariables(scope.options.api);
          GeneralModelService.list(apiPath, params).then(function(response) {
            if (!response) return; //in case http request was cancelled by newer request
            scope.list = response;
            if (scope.options.allowInsert) {
              var addNewItem = {};
              addNewItem[scope.options.searchField] = "[Add New Item]";
              scope.list.push(addNewItem);
            }
            if (scope.options.allowClear) {
              var addNewItem = {};
              addNewItem[scope.options.searchField] = "[clear]";
              scope.list.unshift(addNewItem);

            }
            if (typeof scope.options.defaultIndex === 'number') {
              if (response[scope.options.defaultIndex]) {
                //scope.selected.items = [response[scope.options.defaultIndex]];
                scope.onSelect(response[scope.options.defaultIndex]);
              }
            }
          });
        };

        var unwatch = scope.$watchCollection('[data, options, modelData]', function(results) {
          if (scope.modelData && scope.modelData && scope.options && scope.options.multiple) {
            if (!scope.property.display.sourceModel) {
              unwatch();
              //No sourceModel so try to populate from modelData for items already selected
              if (scope.modelData[scope.property.display.options.relationship]) {
                scope.selected.items = scope.modelData[scope.property.display.options.relationship];
                scope.list = scope.selected.items; //make sure list contains item otherwise won't be displayed
              }
              return;
            }
            //Lookup multiple records that were previously selected
            var sourceModel = Config.serverParams.models[scope.property.display.sourceModel];
            var referenceModel = Config.serverParams.models[scope.options.model];
            var sourceModelName = sourceModel.plural;
            var referenceModelName = referenceModel.plural;
            var sourceId = scope.modelData[scope.property.display.sourceKey];
            if (!sourceId) {
              return;
            }
            unwatch(); //due to late binding need to unwatch here
            GeneralModelService.getMany(sourceModelName, sourceId, scope.options.relationship)
            .then(function(response) {
              if (!response) return;  //in case http request was cancelled
              if (scope.options.api && response.length > 0) {
                //If custom API is provided then use it
                var params = {filter: { where: {}}};
                params.filter.where[scope.options.key] = {inq: []};
                for (var i in response) {
                  var item = response[i];
                  params.filter.where[scope.options.key].inq.push(item[scope.options.key]);
                }
                apiPath = replaceSessionVariables(scope.options.api);
                GeneralModelService.list(apiPath, params).then(function(response) {
                  if (!response) return;  //in case http request was cancelled
                  scope.selected.items = response;
                  scope.list = response;
                });
              } else {
                scope.selected.items = response;
                scope.list = response;
              }
            });

          } else if (scope.data && scope.options && scope.options.model) {
            //Lookup default reference record
            var model = Config.serverParams.models[scope.options.model];
            //unwatch(); //due to late binding need to unwatch here
            GeneralModelService.get(model.plural, scope.data)
            .then(function(response) {
              if (!response) return;  //in case http request was cancelled
              //console.log("default select = " + JSON.stringify(response));
              scope.selected.item = response;
              scope.list = [scope.selected.item]; //make sure list contains item otherwise won't be displayed
              if (scope.onModelChanged) scope.onModelChanged({'$item': scope.selected.item});
            }, function(error) {
                if (scope.options.allowInsert) {
                  //Not found so just add the item
                  var newItem = {};
                  newItem[scope.options.key] = scope.data;
                  newItem[scope.options.searchField] = scope.data;
                  scope.selected.item = newItem;
                  scope.list.push(newItem);
                }

            });
          }
       });

       scope.onSelect = function(item, model) {
         if (scope.options.multiple) {
           //For multi-select add as relationship array objects to modelData (when saving, the CMS relational-upsert.js will handle it)
           scope.selected.items.push(item);
           //Make sure to loop through all items for junctionMeta (previously loaded items will not have junctionMeta populated)
           if (scope.options.junctionMeta) {
             for (var i in scope.selected.items) {
               var item = scope.selected.items[i];
               //meta data for junction table in a many-to-many situation
               item.junctionMeta = scope.options.junctionMeta;
             }
           }
           //Assign to model data
           scope.modelData[scope.options.relationship] = scope.selected.items;
         } else {
           //For single record reference just assign the ID back to data
           scope.data = item[scope.options.key];
           //emit an event when an item is selected
           scope.$emit('onModelFieldReferenceSelect', scope.modelData, scope.key, item);
           var textValue = item[scope.options.searchField];
            if (item && item[scope.options.searchField] == "[Add New Item]") {
              //console.log("should add " + $select.search);
              var value = element.find("input.ui-select-search").val();
              scope.data = value;
              var newItem = {};
              newItem[scope.options.key] = value;
              newItem[scope.options.searchField] = value;
              scope.selected.item = newItem;
              scope.list.push(newItem);
            } else if (item && item[scope.options.searchField] == "[clear]") {
              //console.log("should add " + $select.search);
              scope.data = null;
              textValue = "";
            }

            //For the Model List Edit View we need a way to return back the
            //text value to be displayed. The config.json can specify the rowData
            //and textOutputPath to retrieve the data
            if (scope.rowData && scope.textOutputPath && item[scope.options.searchField]) {
              if (scope.textOutputPath.indexOf(".") > -1) {
                var path = scope.textOutputPath.split(".");
                var obj = scope.rowData;
                for (var i = 0; i < path.length-1; i++) {
                  var property = path[i];
                  if (!obj[property]) obj[property] = {};
                  obj = obj[property];
                }
                obj[path[path.length-1]] = textValue;
              } else {
                scope.rowData[scope.textOutputPath] = textValue;
              }
            }

            setTimeout(function() {
              //Needed in a timeout so the scope.data gets saved
              //before emitting ngGridEventEndCellEdit
              scope.$emit('ngGridEventEndCellEdit');
            }, 1);
         }
       };

       scope.onRemove = function(item, model) {
         if (scope.options.multiple) {
           //Remove item from array
           var index = scope.selected.items.indexOf(item);
           if (index > -1) {
             scope.selected.items.splice(index, 1);
             if (scope.options.junctionMeta) {
             //Make sure to loop through all items for junctionMeta (previously loaded items will not have junctionMeta populated)
               for (var i in scope.selected.items) {
                 var item = scope.selected.items[i];
                 //meta data for junction table in a many-to-many situation
                 item.junctionMeta = scope.options.junctionMeta;
               }
             }
           }
           scope.modelData[scope.options.relationship] = scope.selected.items;
         } else {
           //For single record reference just assign null
           scope.data = null;
         }
       };

       scope.$on('ngGridEventStartCellEdit', function () {
         //When editing focus on the reference button
         element.find("button").trigger("click");
         element.find("input.ui-select-search").focus();
       });

       element.html(getTemplate(scope.options.multiple, scope.options.matchTemplate, scope.options.choiceTemplate)).show();
       $compile(element.contents())(scope);

    }
  };
})

;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import Modal from 'react-modal'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import queryString from 'query-string'
import base64url from 'base64url'
import { decodeToken } from 'jsontokens'
import Image from './Image'
import { makeAuthResponse } from 'blockstack'

function mapStateToProps(state) {
  return {
    localIdentities: state.identities.localIdentities,
    identityKeypairs: state.account.identityAccount.keypairs
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({}, dispatch)
}

const BLOCKSTACK_HANDLER = "web+blockstack"

class AuthModal extends Component {
  static propTypes = {
  }

  constructor(props) {
    super(props)

    let appURI = null
    let appName = null
    let appIcon = null

    const queryDict = queryString.parse(location.search)
    if (queryDict.authRequest) {
      const encodedAuthRequest = queryDict.authRequest.split(BLOCKSTACK_HANDLER + ":").join("")
      const authRequest = decodeToken(encodedAuthRequest)
      const manifest = authRequest.payload.appManifest
      appURI = manifest.start_url
      appName = manifest.name
      appIcon = manifest.icons[0].src
    }

    this.state = {
      appURI: appURI,
      appName: appName,
      appIcon: appIcon
    }

    this.login = this.login.bind(this)
  }

  login() {
    if (Object.keys(this.props.localIdentities).length > 0) {
      const userDomainName = Object.keys(this.props.localIdentities)[0]
      const identity = this.props.localIdentities[userDomainName]
      const profile = identity.profile

      const privateKey = this.props.identityKeypairs[0].key
      const authResponseToken = makeAuthResponse(privateKey, profile, userDomainName)
      window.location = this.state.appURI + '?authResponse=' + authResponseToken
    }
  }

  render() {
    return (

        <Modal
          isOpen={true}
          onRequestClose={this.props.closeModal}
          contentLabel="This is My Modal"
          shouldCloseOnOverlayClick={false}
          style={{overlay: {zIndex: 10}}}
          className="container-fluid">

Sign In Request

            The app "{this.state.appName}" wants to access your basic info

            <Image src={this.state.appIcon} style={{ width: '128px', height: '128px' }}
              fallbackSrc="https://raw.githubusercontent.com/blockstack/blockstack-portal/master/app/images/app-hello-blockstack.png" />

          { Object.keys(this.props.localIdentities).length > 0 ?

              Click below to log in.

              <button className="btn btn-primary btn-block" onClick={this.login}>
                Approve
              </button>
              <Link to="/" className="btn btn-outline-primary btn-block">
                Deny
              </Link>

          :

              You must have created a profile in order to log in.

          }
        </Modal>

    )
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(AuthModal)
<commit_msg>update blockstack handler<commit_after>import React, { Component, PropTypes } from 'react'
import Modal from 'react-modal'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import queryString from 'query-string'
import base64url from 'base64url'
import { decodeToken } from 'jsontokens'
import Image from './Image'
import { makeAuthResponse } from 'blockstack'

function mapStateToProps(state) {
  return {
    localIdentities: state.identities.localIdentities,
    identityKeypairs: state.account.identityAccount.keypairs
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({}, dispatch)
}

const BLOCKSTACK_HANDLER = "blockstack"

class AuthModal extends Component {
  static propTypes = {
  }

  constructor(props) {
    super(props)

    let appURI = null
    let appName = null
    let appIcon = null

    const queryDict = queryString.parse(location.search)
    if (queryDict.authRequest) {
      const encodedAuthRequest = queryDict.authRequest.split(BLOCKSTACK_HANDLER + ":").join("")
      const authRequest = decodeToken(encodedAuthRequest)
      const manifest = authRequest.payload.appManifest
      appURI = manifest.start_url
      appName = manifest.name
      appIcon = manifest.icons[0].src
    }

    this.state = {
      appURI: appURI,
      appName: appName,
      appIcon: appIcon
    }

    this.login = this.login.bind(this)
  }

  login() {
    if (Object.keys(this.props.localIdentities).length > 0) {
      const userDomainName = Object.keys(this.props.localIdentities)[0]
      const identity = this.props.localIdentities[userDomainName]
      const profile = identity.profile

      const privateKey = this.props.identityKeypairs[0].key
      const authResponseToken = makeAuthResponse(privateKey, profile, userDomainName)
      window.location = this.state.appURI + '?authResponse=' + authResponseToken
    }
  }

  render() {
    return (

        <Modal
          isOpen={true}
          onRequestClose={this.props.closeModal}
          contentLabel="This is My Modal"
          shouldCloseOnOverlayClick={false}
          style={{overlay: {zIndex: 10}}}
          className="container-fluid">

Sign In Request

            The app "{this.state.appName}" wants to access your basic info

            <Image src={this.state.appIcon} style={{ width: '128px', height: '128px' }}
              fallbackSrc="https://raw.githubusercontent.com/blockstack/blockstack-portal/master/app/images/app-hello-blockstack.png" />

          { Object.keys(this.props.localIdentities).length > 0 ?

              Click below to log in.

              <button className="btn btn-primary btn-block" onClick={this.login}>
                Approve
              </button>
              <Link to="/" className="btn btn-outline-primary btn-block">
                Deny
              </Link>

          :

              You must have created a profile in order to log in.

          }
        </Modal>

    )
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(AuthModal)
<|endoftext|>