3 page_formats["Select a page format"] = {};
4 page_formats["US Letter PDF"] = {
8 'Select a label format' : {},
59 'CASS [1" x 5 1/4"]': {
69 'MUSA [1" x 5 1/4"]': {
79 'IITA-3 | IITA-2 [2/5" x 5 1/4"]': {
89 '32 Label Size Sticker [1" x 1 1/3"]': {
99 '20 Label Size Sticker [5/6" x 3 3/5"]': {
106 number_of_columns: 2,
113 page_formats["A4 PDF"] = {
117 'Select a label format' : {},
131 page_formats["Zebra printer file"] = {
133 'Select a label format' : {},
151 page_formats["Custom"] = {
153 'Select a label format' : {},
167 var label_options = {};
168 label_options["Select an element type"] = { name: "Select an element type" };
169 label_options["PDFText"] = {
179 label_options["ZebraText"] = {
180 name: "Text (Zebra)",
193 label_options["Code128"] = {
194 name: "1D Barcode (Code128)",
203 label_options["QRCode"] = {
204 name: "2D Barcode (QRCode)",
217 "Courier": "font-family:courier;",
218 "Courier-Bold": "font-family:courier;font-weight:bold;",
219 "Courier-Oblique": "font-family:courier;font-style: oblique;",
220 "Courier-BoldOblique": "font-family:courier;font-weight:bold;font-style: oblique;",
221 "Helvetica": "font-family:helvetica;",
222 "Helvetica-Bold": "font-family:helvetica;font-weight:bold;",
223 "Helvetica-Oblique": "font-family:helvetica;font-style: oblique;",
224 "Helvetica-BoldOblique": "font-family:helvetica;font-weight:bold;font-style: oblique;",
225 "Times": "font-family:times;",
226 "Times-Bold": "font-family:times;font-weight:bold;",
227 "Times-Italic": "font-family:times;font-style: italic;",
228 "Times-BoldItalic": "font-family:times;font-weight:bold;font-style: italic;"
231 var add_fields = {}; // retrieved when data source is selected
232 var num_units = 0; // updated when data source is selected
234 resizer_behaviour = d3.behavior.drag().on(
238 var bb = getTransGroupBounds(target.node())
241 // if (d3.select("#d3-snapping-check").property("checked")){
242 mx = Math.round(mx / doSnap.size) * doSnap.size
243 my = Math.round(my / doSnap.size) * doSnap.size
245 var xexpand = (mx - bb.x) / bb.width
246 var yexpand = (my - bb.y) / bb.height
247 expand = Math.max(xexpand, yexpand)
248 if (!isNaN(expand)) {
249 target.call(doTransform, function(state, selection) {
250 var expX = state.scale[0] * expand;
251 var expY = state.scale[1] * expand;
252 var mi = Math.min(expX * bb.width, expY * bb.height);
254 expX = state.scale[0];
255 expY = state.scale[1];
257 state.scale[0] = expX;
258 state.scale[1] = expY;
261 var newbb = getTransGroupBounds(target.node())
262 d3.select(this.parentNode).select(".selection-tool-outline")
267 height: newbb.height,
269 d3.select(this.parentNode).select(".selection-tool-resizer")
271 x: newbb.x + newbb.width - 3,
272 y: newbb.y + newbb.height - 3,
274 d3.select(this.parentNode).select(".selection-tool-remover")
281 //set up drag behaviour
282 var drag_behaviour = d3.behavior.drag().on(
285 var o = d3.select(this)
286 .call(doTransform, function(state, selection) {
287 state.translate[0] += d3.event.dx;
288 state.translate[1] += d3.event.dy;
292 $(document).ready(function($) {
294 initializeDrawArea();
295 $('#source_type_select').focus();
298 // jQuery('#pagetitle_h3').append(' <a id="label_designer_docs_link" href="http://solgenomics.github.io/sgn/03_managing_breeding_data/03_12.html"><span class="glyphicon glyphicon-info-sign"></span></a>');
300 // Always focus on autofocus elements when modals are opened.
301 $('.modal').on('shown.bs.modal', function() {
302 $(this).find('[autofocus]').folabel
305 $('#d3-save-button').click(function() {
309 $('#design_label_button').click(function() {
310 $("#d3-draw-area").prependTo("#save-labels-display");
311 $(".workflow-complete").click(function() {
312 var title = $(this).children().text();
313 //console.log("workflow element with title "+title+" was just clicked\n");
315 if (title == "Design Your Label") {
316 $("#d3-draw-area").prependTo("#d3-draw-div");
317 } else if (title == "More Options, Save, And Download") {
318 $("#d3-draw-area").prependTo("#save-labels-display");
322 $("ol.workflow-prog li").click(function() {
323 var title = $(this).children().text();
324 //console.log("workflow element with title "+title+" was just clicked\n");
325 if (title == "More Options, Save, And Download") {
326 $("#d3-draw-area").prependTo("#save-labels-display");
332 getDataSourceSelect();
334 $("#edit_additional_settings").on("click", function() {
335 $('#editAdditionalSettingsModal').modal('show');
338 $(document).on("change", "#source_select", function() {
340 var name = jQuery('#source_select :selected').text();
341 jQuery('#selected_data_source').text(name);
343 var data_type = $('#source_select :selected').parent().attr('label');
345 // updateFields(data_type, this.value, '');
347 if (data_type == 'Field Trials') {
349 url: '/ajax/breeders/trial/'+this.value+'/has_data_levels',
351 beforeSend: function() {
354 complete: function() {
356 jQuery('#page_format').focus();
358 success: function(response) {
359 var html = '<select class="form-control" id="label_designer_data_level" ><option value="" selected>Select a Level</option><option value="plots">Plot</option>';
360 if(response.has_plants){
361 html = html + '<option value="plants">Plant</option>';
363 if(response.has_subplots){
364 html = html + '<option value="subplots">Subplot</option>';
366 if(response.has_tissue_samples){
367 html = html + '<option value="tissue_samples">Tissue Sample</option>';
369 html = html + '</select>';
370 jQuery('#label_designer_data_level_select_div').html(html);
371 jQuery("#label_designer_data_level").focus();
373 error: function(response) {
374 alert('There was a problem checking the data levels available for your field trial. Please contact us.');
377 } else if (data_type == 'Genotyping Plates') {
379 jQuery('#label_designer_data_level_select_div').html('<select class="form-control" id="label_designer_data_level" ><option value="" selected>Select a Level</option><option value="plate">Plate</option></select>');
380 jQuery("#label_designer_data_level").focus();
382 } else if (data_type == 'Crossing Experiments') {
384 jQuery('#label_designer_data_level_select_div').html('<select class="form-control" id="label_designer_data_level" ><option value="" selected>Select a Level</option><option value="crosses">Cross</option></select>');
385 jQuery("#label_designer_data_level").focus();
387 } else if ((data_type == 'Lists') || (data_type == 'Public Lists')) {
389 var html = '<select class="form-control" id="label_designer_data_level" ><option value="" selected>Select a Level</option><option value="list">List Items</option>';
390 // Check list type, if Plot, Plant, or Tissue Sample add details option
391 var name = $('#source_select :selected').text();
395 data: { 'name': name },
396 beforeSend: function() {
399 complete: function() {
401 jQuery('#page_format').focus();
403 success: function(response) {
404 if ( response.list_type === 'trials' ) {
405 html += '<option value="plots">Plot Details</option>';
406 } else if (response.list_type == 'plots') {
407 html = html + '<option value="plots">Plot Details</option>';
408 } else if (response.list_type == 'subplots') {
409 html = html + '<option value="subplots">Subplot Details</option>';
410 } else if (response.list_type == 'plants') {
411 html = html + '<option value="plants">Plant Details</option>';
412 } else if (response.list_type == 'tissue_samples') {
413 html = html + '<option value="tissue_samples">Tissue Sample Details</option>';
414 } else if (response.list_type == 'crosses') {
415 html = html + '<option value="crosses">Cross Details</option>';
416 } else if (response.list_type == 'identifier_generation') {
417 // remove list item select options and add options for each id batch
418 html = '<select class="form-control" id="label_designer_data_level" ><option value="" selected>Select a Level</option>';
419 var lo = new CXGN.List();
420 var list_data = lo.getListData(response.list_id);
421 var elements = list_data.elements;
422 var identifier_object = JSON.parse(elements[0][1]);
423 var records = identifier_object.records;
424 for (var x = 0; x < records.length; x++) {
425 var generated_identifiers = records[x].generated_identifiers;
426 if (generated_identifiers) {
427 //console.log("current identifiers are: "+generated_identifiers);
428 var min = generated_identifiers.shift();
429 var max = generated_identifiers.pop();
430 var id = records[x].next_number;
431 html = html + '<option value="batch-'+id+'">ID Batch '+min+' - '+max+'</option>';
435 html = html + '</select>';
436 jQuery('#label_designer_data_level_select_div').html(html);
437 jQuery("#label_designer_data_level").focus();
439 error: function(response) {
440 alert('There was a problem checking the data levels available for your field trial. Please contact us.');
446 jQuery(document).on('change', '#label_designer_data_level', function(){
447 var data_type = $('#source_select :selected').parent().attr('label');
448 var source_id = jQuery('#source_select').val();
450 var name = jQuery('#label_designer_data_level :selected').text();
451 jQuery('#selected_data_level').text(name);
453 if (this.value) { jQuery('#select_datasource_button').prop('disabled', false); }
454 updateFields(data_type, source_id, this.value);
457 jQuery(document).on('change', 'input[type=radio][name=optradio]', function(){
458 if (this.value == 'saved') {
459 document.getElementById("design_list").style.display = "inline";
461 document.getElementById("design_list").style.display = "none";
465 var page_format_select = d3.select("#page_format");
466 page_format_select.on("input", function() {
467 var page = d3.select(this).node().value;
468 if (!page || page == 'Select a page format') {
470 d3.select("#label_format").selectAll("option").remove();
472 switchPageDependentOptions(page); // show correct download and text options
476 $('#label_format').change(function() {
477 var label = $(this).find('option:selected').val();
478 if (!label || label == 'Select a label format') {
480 jQuery('#select_layout_button').prop('disabled',true)
482 switchLabelDependentOptions(label);
486 d3.select("#d3-apply-custom-label-size").on("click", function() {
488 //save and apply custom label size
489 jQuery('#select_layout_button').prop('disabled', false)
490 var page = d3.select("#page_format").node().value;
491 var custom_label = page_formats[page].label_sizes['Custom'];
493 custom_label.label_width = document.getElementById("label_width").value;
494 custom_label.label_height = document.getElementById("label_height").value;
495 changeLabelSize(custom_label.label_width, custom_label.label_height);
496 $("#d3-add-and-download-div").removeAttr('style');
498 $('#d3-add-type-input').focus();
501 $('#d3-add-field-input').change(function() {
502 $("#d3-add-size-input").focus();
505 $("#d3-custom-field").on("click", function() {
506 $("#d3-custom-input").val('');
507 $('#customFieldModal').modal('show');
510 d3.select("#d3-custom-field-save")
511 .on("click", function() {
512 var custom_text = $("#d3-custom-input").val();
513 var custom_value = custom_text.replace(/\{(.*?)\}/g, function(match, token) {
514 //console.log("token is "+token);
515 if (token.match(/Number:/)) {
516 var parts = token.split(':');
519 return add_fields[token];
523 $("#d3-add-field-input").append($('<option>', {
530 $('#d3-add-type-input').change(function() {
531 if (!this.value || this.value == 'Select an element type') {
534 switchTypeDependentOptions(this.value);
535 $("#d3-add-field-input").focus();
540 $("#d3-edit-additional-settings").on("click", function() {
541 saveAdditionalOptions(
542 document.getElementById("top_margin").value,
543 document.getElementById("left_margin").value,
544 document.getElementById("horizontal_gap").value,
545 document.getElementById("vertical_gap").value,
546 document.getElementById("number_of_columns").value,
547 document.getElementById("number_of_rows").value,
548 document.getElementById("plot_filter").value,
549 document.getElementById("sort_order_1").value,
550 document.getElementById("sort_order_2").value,
551 document.getElementById("sort_order_3").value,
552 document.getElementById("copies_per_plot").value
556 $("#d3-add").on("click", function() {
557 var type = document.getElementById("d3-add-type-input").value;
558 if (!type || type == 'Select an element type') {
559 alert("A valid type must be selected.");
562 var field = $('#d3-add-field-input').find('option:selected').text();
563 if (!field || field == 'Select a field') {
564 alert("A valid field must be selected.");
567 // check if add_fields[field] is defined. If so, add {}s
568 if (add_fields[field]) {
569 field = "{"+field+"}";
571 var text = document.getElementById("d3-add-field-input").value;
572 var size = document.getElementById("d3-add-size-input").value;
573 var font = document.getElementById("d3-add-font-input").value || 'Courier';
574 addToLabel(field, text, type, size, font);
575 jQuery('#design_label_button').prop('disabled', false)
578 $("#d3-pdf-button, #d3-zpl-button").on("click", function() {
579 var design = retrievePageParams();
580 var download_type = $(this).val();
581 console.log("Design is "+JSON.stringify(design));
583 // if over 1,000 to download, throw warning with editable start and end number and text recommending to download in batches of 1,000 or less
584 var label_count = num_units * design.copies_per_plot;
585 var labels_to_download = design.labels_to_download;
586 if (label_count < 1000 || (labels_to_download && labels_to_download < 1000)) {
587 downloadLabels(design, download_type);
588 } else if (label_count >= 1000) {
589 //show warning with editable inputs for start and end
590 var message = "You are trying to download "+label_count+ " labels ("+label_count+" "+jQuery('#label_designer_data_level :selected').text()+" x "+design.copies_per_plot+" copy(ies) each). Downloading this many at a time may result in slow speeds. Please use the input boxes below to control how many are downloaded at a time.";
591 $("#batch_download_message").text(message);
592 $("#d3-batch-download-submit").val(download_type);
593 $('#batchDownloadModal').modal('show');
598 $("#d3-batch-download-submit").on("click", function() {
599 var download_type = $(this).val();
600 var design = retrievePageParams();
601 downloadLabels(design, download_type);
606 function downloadLabels (design, download_type) {
607 var label_elements = document.getElementsByClassName('label-element');
608 label_elements = Array.prototype.slice.call(label_elements); // convert to array
609 if (label_elements.length < 1) {
610 alert("No elements in the design. Please add design elements before downloading");
613 var data_type = $('#source_select :selected').parent().attr('label');
614 var source_id = $("#source_select").val();
615 var source_name = $("#source_select :selected").text();
616 //console.log("Id is "+source_id+" and name is "+source_name);
617 if (!source_id || source_id == 'Please select a trial' || source_id == 'Select a plot list') {
618 alert("No data source selected. Please select a data source before downloading");
623 alert("No design. Please define a design before downloading");
626 design.label_elements = label_elements.filter(checkIfVisible).map(getLabelDetails);
628 var design_json = JSON.stringify(design);
629 console.log("design is"+design_json);
630 var data_level = jQuery('#label_designer_data_level').val();
632 //send to server to build pdf file
634 url: '/tools/label_designer/download',
638 'download_type': download_type,
639 'data_type' : data_type,
640 'source_id': source_id,
641 'source_name': source_name,
642 'design_json': design_json,
643 'data_level': data_level
645 beforeSend: function() {
646 console.log("Downloading "+download_type+" file . . . ");
649 complete: function() {
652 success: function(response) {
653 if (response.error) {
655 alert(response.error);
657 console.log("Got file "+response.filename);
659 window.location.href = "/download/" + response.filename;
662 error: function(request, status, err) {
664 alert("Error. Unable to download labels.");
669 function updateFields(data_type, source_id, data_level){
671 //console.log("running update fields");
672 if (data_type.match(/List/)) {
673 jQuery('#sort_order_1').val('list_order');
677 url: '/tools/label_designer/retrieve_longest_fields',
681 data_type: data_type,
682 source_id: source_id,
683 data_level: data_level
685 beforeSend: function() {
688 complete: function() {
690 jQuery('#page_format').focus();
692 success: function(response) {
693 if (response.error) {
694 alert("An error occured while retrieving the design elements of this trial: " + JSON.stringify(response.error));
695 getDataSourceSelect();
697 add_fields = response.fields;
699 // if reps, add reps as options for filtering
700 reps = response.reps;
701 num_units = response.num_units;
703 addSortOrders(add_fields, data_type, data_level);
704 createAdders(add_fields);
705 initializeCustomModal(add_fields);
708 if ( d3.select("#page_format").node().value && d3.select("#page_format").node().value != 'Select a page format') {
709 switchPageDependentOptions( d3.select("#page_format").node().value );
711 var page_format_select = d3.select("#page_format");
712 page_format_select.selectAll("option")
713 .data(Object.keys(page_formats))
714 .enter().append("option")
721 error: function(request, status, err) {
722 alert("Unable to retrieve design elements of this trial. Please confirm this trial has a design, or try again with a different trial. Please contact us!");
727 function changeLabelSize(width, height) {
728 var width = width * 2.83 //convert from pixels to dots (72/inch to 8/mm)
729 var height = height * 2.83 //convert from pixels to dots (72/inch to 8/mm)
730 d3.select(".label-template").attr("viewBox", "0 0 " + width + " " + height);
731 d3.select(".d3-bg").attr("width", width);
732 d3.select(".d3-bg").attr("height", height);
736 function initializeDrawArea() {
739 d3.select(".label-template").remove();
740 var svg = d3.select("#d3-draw-area")
742 .classed("label-template", true)
745 viewBox: "0 0 510 204"
746 }).classed("d3-draw-svg", true);
749 var rect = svg.append('rect')
750 .classed("d3-bg", true)
758 "stroke-width": "2px"
760 .on("click", clearSelection);
762 .classed("d3-intro-text", true)
767 "text-anchor": "middle",
768 "alignment-baseline": "central",
770 .text('Label Design Area')
772 .classed("d3-intro-text", true)
777 "style": "font-style: oblique;",
778 "text-anchor": "middle",
779 "alignment-baseline": "central",
781 .text('Set source, page, and label formats above to start designing.');
784 var grid = svg.append("g").classed("d3-bg-grid", true);
785 grid.append('g').classed("d3-bg-grid-vert", true);
786 grid.append('g').classed("d3-bg-grid-horz", true);
791 function draggable(d, i) {
792 var bb = this.node().getBBox();
797 .call(doTransform, function(state, selection) {
798 state.translate = [-bb.x, -bb.y]
800 .call(drag_behaviour);
803 function selectable(selection, resizeable) {
804 this.on("mousedown", function() {
805 d3.select(".selection-tools").remove();
807 this.on("click", function() {
808 var o = d3.select(".d3-draw-svg");
809 var bb = getTransGroupBounds(this);
810 var target = d3.select(this);
811 var tools = o.append('g')
812 .classed("selection-tools", true)
815 .classed("selection-tool-outline", true)
823 "stroke-dasharray": ("3,3"),
827 .classed("selection-tool-remover", true)
836 .on("click", function() {
837 d3.event.stopPropagation();
843 .classed("selection-tool-resizer", true)
844 .on("click", function() {
845 d3.event.stopPropagation();
848 x: bb.x + bb.width - 4,
849 y: bb.y + bb.height - 4,
854 }).call(resizer_behaviour);
859 function doTransform(selection, transformFunc) {
860 var state = d3.transform(selection.attr("transform"));
861 transformFunc(state, selection);
862 selection.attr("transform", state.toString());
865 function clearSelection() {
866 d3.select(".selection-tools").remove();
869 function getTransGroupBounds(node) {
870 var bb = node.getBBox()
871 var state = d3.transform(d3.select(node).attr("transform"));
872 bb.x = bb.x * state.scale[0]
873 bb.y = bb.y * state.scale[1]
874 bb.width = bb.width * state.scale[0]
875 bb.height = bb.height * state.scale[1]
876 bb.x += state.translate[0]
877 bb.y += state.translate[1]
881 function updateGrid(size) {
882 //set snapping distance
885 var width = document.getElementById("d3-label-area").viewBox.baseVal.width; //.getBoundingClientRect().width();
886 var height = document.getElementById("d3-label-area").viewBox.baseVal.height;
888 for (var x = size; x < width; x += size) {
891 var vert = d3.select(".d3-bg-grid-vert").selectAll("line")
894 vert.enter().append('line')
898 stroke: "rgba(0,0,0,0.2)",
901 .attr("x1", function(d) {
904 .attr("x2", function(d) {
907 .on("click", clearSelection);
910 for (var y = size; y < height; y += size) {
913 var horz = d3.select(".d3-bg-grid-horz").selectAll("line")
916 horz.enter().append('line')
920 stroke: "rgba(0,0,0,0.2)",
923 .attr("y1", function(d) {
926 .attr("y2", function(d) {
929 .on("click", clearSelection);
932 function doSnap(state, selection) {
933 var bb = getTransGroupBounds(selection.node());
934 var left_snap_d = (Math.round(bb.x / doSnap.size)) * doSnap.size - bb.x
935 var right_snap_d = (Math.round((bb.x + bb.width) / doSnap.size) * doSnap.size - bb.width) - bb.x
936 var top_snap_d = (Math.round(bb.y / doSnap.size)) * doSnap.size - bb.y
937 var bottom_snap_d = (Math.round((bb.y + bb.height) / doSnap.size) * doSnap.size - bb.height) - bb.y
938 state.translate[0] += Math.abs(left_snap_d) < Math.abs(right_snap_d) ? left_snap_d : right_snap_d
939 state.translate[1] += Math.abs(top_snap_d) < Math.abs(bottom_snap_d) ? top_snap_d : bottom_snap_d
942 function getDataSourceSelect() {
943 get_select_box('label_data_source_types', 'data_source_type', {
944 name: 'source_type_select',
945 id: 'source_type_select',
946 default: 'Select a type of data source for the labels',
949 updateDataSourceSelect();
950 jQuery(document).off("change").on("change", "#source_type_select", updateDataSourceSelect);
953 function updateDataSourceSelect() {
954 get_select_box('label_data_sources', 'data_source',
956 name: 'source_select',
958 default: 'Select a data source for the labels',
960 type: jQuery("#source_type_select option:selected").val()
965 function switchPageDependentOptions(page) {
966 // load label size and label field options based on page type
967 var label_sizes = page_formats[page].label_sizes;
968 d3.select("#label_format").selectAll("option").remove();
969 d3.select("#label_format").selectAll("option")
970 .data(Object.keys(label_sizes))
971 .enter().append("option")
976 d3.select("#d3-add-type-input").selectAll("option").remove();
977 d3.select("#d3-add-type-input").selectAll("option")
978 .data(Object.keys(label_options))
979 .enter().append("option")
981 return label_options[d].name
983 .attr("value", function(d) {
987 if (page == 'Zebra printer file') { // disable PDF text option and pdf download, clear pdf text elements
988 $("#d3-add-type-input option[value='PDFText']").remove();
989 switchTypeDependentOptions('ZebraText');
990 document.getElementById("d3-pdf-button").style.display = "none";
991 document.getElementById("d3-zpl-button").style.display = "inline";
992 document.getElementById("d3-add-font-input").value = "Courier"; // default for Zebra
994 var label_elements = document.getElementsByClassName("label-element");
995 label_elements = Array.prototype.slice.call(label_elements); // convert to array
996 for (var i=0; i<label_elements.length; i++) {
997 var element = label_elements[i];
998 if (element.getAttribute("type") != "ZebraText") { // remove all non-Zebra elements, barcodes too in case they have been scaled
999 element.parentNode.removeChild(element);
1003 } else { // disable Zebra text option and zpl download, clear zpl text elements
1004 switchTypeDependentOptions('PDFText');
1005 $("#d3-add-type-input option[value='ZebraText']").remove();
1006 document.getElementById("d3-zpl-button").style.display = "none";
1007 document.getElementById("d3-pdf-button").style.display = "inline";
1009 var label_elements = document.getElementsByClassName("label-element");
1010 label_elements = Array.prototype.slice.call(label_elements); // convert to array
1011 for (var i=0; i<label_elements.length; i++) {
1012 var element = label_elements[i];
1013 if (element.getAttribute("type") == "ZebraText") { // remove only text, barcodes can stay
1014 element.parentNode.removeChild(element);
1019 if (page == 'Custom') { // show Custom inputs and attach handlers
1020 document.getElementById("d3-custom-dimensions-div").style.display = "inline";
1021 document.getElementById("d3-page-custom-dimensions-div").style.visibility = "visible";
1022 document.getElementById("d3-label-custom-dimensions-div").style.visibility = "visible";
1023 document.getElementById("label_format").value = 'Custom';
1024 $('#page_width').focus();
1026 $('#page_width').on("change", function() {
1027 page_formats[page].page_width = this.value;
1030 $('#page-height').on("change", function() {
1031 page_formats[page].page_height = this.value;
1033 } else { //hide page custom input
1034 document.getElementById("d3-page-custom-dimensions-div").style.visibility = "hidden";
1035 $('#label_format').focus();
1038 if ( page != 'Custom' && document.getElementById("label_format").value != 'Custom') { //hide all Custom inputs
1039 document.getElementById("d3-custom-dimensions-div").style.display = "none";
1040 document.getElementById("d3-page-custom-dimensions-div").style.visibility = "hidden";
1041 document.getElementById("d3-label-custom-dimensions-div").style.visibility = "hidden";
1046 function switchLabelDependentOptions(label) {
1048 var page = d3.select("#page_format").node().value;
1049 var label_sizes = page_formats[page].label_sizes;
1051 if (label == 'Custom') {
1052 document.getElementById("d3-custom-dimensions-div").style.display = "inline";
1053 document.getElementById("d3-label-custom-dimensions-div").style.visibility = "visible";
1054 $('#label_width').focus();
1056 jQuery('#select_layout_button').prop('disabled', false)
1057 document.getElementById("d3-custom-dimensions-div").style.display = "none";
1058 document.getElementById("d3-label-custom-dimensions-div").style.visibility = "hidden";
1059 changeLabelSize( label_sizes[label].label_width, label_sizes[label].label_height);
1060 $("#d3-add-and-download-div").removeAttr('style');
1061 $('#d3-add-type-input').focus();
1064 //set addtional options
1065 document.getElementById("top_margin").value = label_sizes[label].top_margin;
1066 document.getElementById("left_margin").value = label_sizes[label].left_margin;
1067 document.getElementById("horizontal_gap").value = label_sizes[label].horizontal_gap;
1068 document.getElementById("vertical_gap").value = label_sizes[label].vertical_gap;
1069 document.getElementById("number_of_columns").value = label_sizes[label].number_of_columns;
1070 document.getElementById("number_of_rows").value = label_sizes[label].number_of_rows;
1073 function switchTypeDependentOptions(type){
1075 var sizes = label_options[type].sizes;
1076 if (type == "PDFText") {
1078 // set up font select
1079 d3.select("#d3-add-font-input").selectAll("option").remove();
1080 d3.select("#d3-add-font-input").selectAll("option")
1081 .data(Object.keys(font_styles).sort())
1082 .enter().append("option")
1086 .attr("style", function(d) {
1087 return font_styles[d]
1089 .attr("value", function(d) {
1092 document.getElementById("d3-add-font-div").style.visibility = "visible";
1094 // set up size input and slider
1095 $("#d3-add-size-input").replaceWith('<input type="number" id="d3-add-size-input" class="form-control"></input>');
1096 var size_input = d3.select("#d3-add-size-input");
1097 var size_slider = d3.select("#d3-add-size-slider");
1098 size_input.attr(sizes);
1099 size_slider.attr(sizes);
1100 size_slider.on("input", function() {
1101 size_input.property("value", this.value)
1103 size_input.on("change", function() {
1104 size_slider.node().value = this.value;
1106 $("#d3-add-size-slider").show();
1109 document.getElementById("d3-add-font-div").style.visibility = "hidden";
1110 $("#d3-add-size-input").replaceWith('<select id="d3-add-size-input" class="form-control"></select>  ');
1111 d3.select("#d3-add-size-input").selectAll("option")
1112 .data(Object.keys(sizes))
1113 .enter().append("option")
1117 .attr("value", function(d) {
1120 $("#d3-add-size-slider").hide();
1124 function addToLabel(field, text, type, size, font, x, y, width, height) {
1125 //console.log("Field is: "+field+" and text is: "+text+" and type is: "+type+" and size is: "+size+" and font is: "+font);
1126 svg = d3.select(".d3-draw-svg");
1128 //get x,y coords and scale
1129 if ((typeof x || typeof y ) === 'undefined') {
1130 x = document.getElementById("d3-label-area").viewBox.baseVal.width/2;
1131 y = document.getElementById("d3-label-area").viewBox.baseVal.height/2;
1133 //console.log(" X is: "+x+" and y is: "+y);
1135 //count existing elements
1136 var label_elements = document.getElementsByClassName('label-element');
1137 label_elements = Array.prototype.slice.call(label_elements); // convert to array
1138 var count = label_elements.length;
1140 //set up new element
1141 var new_element = svg.append("g")
1142 .classed("draggable", true)
1143 .classed("selectable", true)
1149 //console.log("Adding barcode of type "+type);
1150 //add barcode specific attributes
1152 var page = d3.select("#page_format").node().value;
1153 new_element.classed("barcode", true)
1154 if ( page.match(/Zebra/) ) {
1155 new_element.call(selectable, false);
1157 new_element.call(selectable, true);
1159 var img = new Image();
1160 img.src = "/tools/label_designer/preview?content=" + encodeURIComponent(text) + "&type=" + encodeURIComponent(type) + "&size=" + encodeURIComponent(size);
1161 img.onload = function() {
1162 var element_width = width || this.width;
1163 var element_height = height || this.height;
1164 x = x - (element_width /2);
1165 y = y - (element_height /2);
1166 //console.log("Final x is "+x+" and final y is "+y);
1167 new_element.call(doTransform, function(state, selection) {
1168 state.translate = [x,y]
1170 .append("svg:image")
1172 "id": "element"+count,
1173 "class": "label-element",
1177 "width": element_width,
1178 "height": element_height,
1179 "href": "/tools/label_designer/preview?content=" + encodeURIComponent(text) + "&type=" + encodeURIComponent(type) + "&size=" + encodeURIComponent(size),
1186 //add text specific attributes
1187 //console.log("Adding text of type "+type);
1188 var font_size = parseInt(size);
1189 if (type =="ZebraText") {
1190 font_size = font_size + parseInt(font_size/9);
1192 new_element.classed("text-box", true)
1193 .call(selectable, false)
1194 .call(doTransform, function(state, selection) {
1195 state.translate = [x,y]
1199 "id": "element"+count,
1200 "class": "label-element",
1204 "font-size": font_size,
1206 "style": font_styles[font],
1207 "text-anchor": "middle",
1208 "alignment-baseline": "middle",
1211 //console.log("Field is: "+field+" and size is: "+size+" and type is: "+type+" and font size is: "+font_size+" and font is: "+font+" and style is: "+font_styles[font]+" and text is: "+text);
1217 function saveAdditionalOptions(top_margin, left_margin, horizontal_gap, vertical_gap, number_of_columns, number_of_rows, plot_filter, sort_order_1, sort_order_2, sort_order_3, copies_per_plot) {
1218 // save options in javascript object and in html elements
1219 var page = d3.select("#page_format").node().value;
1220 var label = d3.select("#label_format").node().value;
1221 page_formats[page].label_sizes[label].top_margin = top_margin;
1222 page_formats[page].label_sizes[label].left_margin = left_margin;
1223 page_formats[page].label_sizes[label].horizontal_gap = horizontal_gap;
1224 page_formats[page].label_sizes[label].vertical_gap = vertical_gap;
1225 page_formats[page].label_sizes[label].number_of_columns = number_of_columns;
1226 page_formats[page].label_sizes[label].number_of_rows = number_of_rows;
1227 page_formats[page].label_sizes[label].plot_filter = plot_filter;
1228 page_formats[page].label_sizes[label].sort_order_1 = sort_order_1;
1229 page_formats[page].label_sizes[label].sort_order_2 = sort_order_2;
1230 page_formats[page].label_sizes[label].sort_order_3 = sort_order_3;
1231 page_formats[page].label_sizes[label].copies_per_plot = copies_per_plot;
1232 document.getElementById("top_margin").value = top_margin;
1233 document.getElementById("left_margin").value = left_margin;
1234 document.getElementById("horizontal_gap").value = horizontal_gap;
1235 document.getElementById("vertical_gap").value = vertical_gap;
1236 document.getElementById("number_of_columns").value = number_of_columns;
1237 document.getElementById("number_of_rows").value = number_of_rows;
1238 document.getElementById("plot_filter").value = plot_filter || 'all';
1239 document.getElementById("sort_order_1").value = sort_order_1;
1240 document.getElementById("sort_order_2").value = sort_order_2;
1241 document.getElementById("sort_order_3").value = sort_order_3;
1242 document.getElementById("copies_per_plot").value = copies_per_plot;
1245 function dragSnap() {
1246 d3.select(this).call(doTransform, doSnap);
1249 function createAdders(add_fields) {
1250 // load type select options
1251 d3.select("#d3-add-type-input").selectAll("option").remove();
1252 d3.select("#d3-add-type-input").selectAll("option")
1253 .data(Object.keys(label_options))
1254 .enter().append("option")
1256 return label_options[d].name
1258 .attr("value", function(d) {
1262 //load field options
1263 d3.select("#d3-add-field-input").selectAll("option").remove();
1264 d3.select("#d3-add-field-input").selectAll("option")
1265 .data(["Select a field", ...Object.keys(add_fields).sort()])
1266 .enter().append("option")
1270 .attr("value", function(d) {
1271 return add_fields[d]
1276 function addPlotFilter(reps) {
1278 Object.keys(reps).forEach(function(key) {
1279 var newkey = "Rep "+key+" only";
1283 reps['All'] = 'all';
1285 d3.select("#plot_filter").selectAll("option").remove();
1286 d3.select("#plot_filter").selectAll("option")
1287 .data(Object.keys(reps).sort())
1288 .enter().append("option")
1292 .attr("value", function(d) {
1298 function addSortOrders(add_fields, data_type, data_level) {
1300 // Set type-specific sorting options
1301 let data_type_fields = [];
1302 // Sort by trial layout for plot-level labels...
1303 if ( ['Field Trials', 'Lists', 'Public Lists'].includes(data_type) && data_level === 'plots' ) {
1304 data_type_fields = ['Trial Layout: Plot Order'];
1308 d3.selectAll("#sort_order_1, #sort_order_2, #sort_order_3").selectAll("option").remove();
1309 d3.selectAll("#sort_order_1").selectAll("option")
1310 .data(["Select a field", ...data_type_fields, ...Object.keys(add_fields).sort()])
1311 .enter().append("option")
1315 .attr("value", function(d) {
1318 d3.selectAll("#sort_order_2, #sort_order_3").selectAll("option")
1319 .data(["Select a field", ...Object.keys(add_fields).sort()])
1320 .enter().append("option")
1324 .attr("value", function(d) {
1327 jQuery("#sort_order_1").off("change").on("change", () => {
1328 const sel = jQuery("#sort_order_1").val();
1329 if ( sel === 'Select a field' ) {
1330 jQuery("#sort_order_2_container, #sort_order_3_container").hide();
1331 jQuery("#sort_order_layout_order_container, #sort_order_layout_start_container").hide();
1333 else if ( sel === 'Trial Layout: Plot Order' ) {
1334 jQuery("#sort_order_2_container, #sort_order_3_container").hide();
1335 jQuery("#sort_order_layout_order_container, #sort_order_layout_start_container").show();
1338 jQuery("#sort_order_2_container").show();
1339 jQuery("#sort_order_layout_order_container, #sort_order_layout_start_container").hide();
1342 jQuery("#sort_order_2").off("change").on("change", () => {
1343 const sel = jQuery("#sort_order_2").val();
1344 if ( sel === 'Select a field' ) {
1345 jQuery("#sort_order_3_container").hide();
1348 jQuery("#sort_order_3_container").show();
1353 function checkIfVisible(element) {
1354 var label_width = document.getElementById("d3-label-area").viewBox.baseVal.width;
1355 var label_height = document.getElementById("d3-label-area").viewBox.baseVal.height;
1356 var transform_attributes = parseTransform(element.parentNode.getAttribute('transform')); // return transform attributes as an object
1357 var coords = transform_attributes.translate;
1358 var type = element.getAttribute("type");
1359 if (coords[0] > label_width || coords[1] > label_height) { // ignore elements not visible in draw area
1360 return false; // skip
1366 function getLabelDetails(element) {
1367 var transform_attributes = parseTransform(element.parentNode.getAttribute('transform')); // return transform attributes as an object
1368 //console.log("Transform attributes are: "+JSON.stringify(transform_attributes));
1369 var coords = transform_attributes.translate;
1370 var scale = transform_attributes.scale || new Array(1,1);
1371 var rect = element.getBBox();
1372 var width = rect.width * scale[0];
1373 var height = rect.height * scale[1];
1374 //console.log("Height is: "+height+" and width is: "+width);
1375 var type = element.getAttribute("type");
1378 if (type.match(/Text/)) {
1379 x = parseInt(coords[0])
1380 y = parseInt(coords[1])
1382 x = parseInt(coords[0]) + (width/2);
1383 y = parseInt(coords[1]) + (height/2);
1391 value: element.getAttribute("value"),
1392 type: element.getAttribute("type"),
1393 font: element.getAttribute("font") || 'Courier',
1394 size: element.getAttribute("size")
1398 function parseTransform(transform) {
1399 var attribute_object = {};
1400 var attributes = transform.split(')');
1402 for (var i = 0; i < attributes.length; i++) {
1403 var attribute = attributes[i];
1404 var parts = attribute.split('(');
1405 var name = parts.shift();
1406 var values = parts.join(',');
1407 attribute_object[name] = values.split(',');
1410 return attribute_object;
1413 function retrievePageParams() {
1415 var page = d3.select("#page_format").node().value;
1416 if (!page || page == 'Select a page format') {
1417 alert("No page format select. Please select a page format");
1420 var label = d3.select("#label_format").node().value;
1421 if (!label || label == 'Select a label format') {
1422 alert("No label format select. Please select a label format");
1425 var label_sizes = page_formats[page].label_sizes;
1429 page_width: page_formats[page].page_width || document.getElementById("page_width").value,
1430 page_height: page_formats[page].page_height || document.getElementById("page_height").value,
1431 left_margin: label_sizes[label].left_margin,
1432 top_margin: label_sizes[label].top_margin,
1433 horizontal_gap: label_sizes[label].horizontal_gap,
1434 vertical_gap: label_sizes[label].vertical_gap,
1435 number_of_columns: label_sizes[label].number_of_columns,
1436 number_of_rows: label_sizes[label].number_of_rows,
1437 plot_filter: document.getElementById("plot_filter").value,
1438 sort_order_1: document.getElementById("sort_order_1").value,
1439 sort_order_2: document.getElementById("sort_order_2").value,
1440 sort_order_3: document.getElementById("sort_order_3").value,
1441 sort_order_layout_order: document.getElementById("sort_order_layout_order").value,
1442 sort_order_layout_start: document.getElementById("sort_order_layout_start").value,
1443 copies_per_plot: document.getElementById("copies_per_plot").value,
1444 labels_to_download: document.getElementById("label_designer_labels_to_download").value,
1445 start_number: document.getElementById("label_designer_start_number").value,
1446 end_number: document.getElementById("label_designer_end_number").value,
1447 label_format: label,
1448 label_width: label_sizes[label].label_width,
1449 label_height: label_sizes[label].label_height,
1450 start_col: document.getElementById("start_col").value,
1451 start_row: document.getElementById("start_row").value,
1457 function initializeCustomModal(add_fields) {
1458 //load field options
1459 d3.select("#d3-custom-add-field-input").selectAll("option").remove();
1460 d3.select("#d3-custom-add-field-input").selectAll("option")
1461 .data(["Select a field", ...Object.keys(add_fields).sort()])
1462 .enter().append("option")
1466 .attr("value", function(d) {
1467 return add_fields[d]
1470 $('#d3-custom-add-field-input').on("change", function() {
1472 var value = $(this).find('option:selected').text();
1473 if (!value || value == 'Select a field') {
1476 var custom_field = $("#d3-custom-input").val() + "{" + value + "}";
1477 $("#d3-custom-input").val(custom_field);
1482 $("#d3-custom-preview").on("click", function() {
1483 var custom_field = $("#d3-custom-input").val();
1484 var result = custom_field.replace(/\{(.*?)\}/g, function(match, token) {
1485 console.log("token is "+token);
1486 if (token.match(/Number:/)) {
1487 var parts = token.split(':');
1490 return add_fields[token];
1493 $("#d3-custom-content").text(result);
1496 $("#d3-add-number").on("click", function() {
1497 var custom_field = $("#d3-custom-input").val() + "{Number:" + $('#start_number').val() +":"+ $('#increment_number').val()+"}";
1498 $("#d3-custom-input").val(custom_field);
1503 function enableDrawArea() {
1504 var intro_elements = document.getElementsByClassName("d3-intro-text");
1505 for (var i=0; i<intro_elements.length; i++) { intro_elements[i].style.display = "none"; }
1506 var label_elements = document.getElementsByClassName("label-element");
1507 for(var i=0; i<label_elements.length; i++) { label_elements[i].style.display = "inline"; }
1510 function disableDrawArea() {
1511 var intro_elements = document.getElementsByClassName("d3-intro-text");
1512 for (var i=0; i<intro_elements.length; i++) { intro_elements[i].style.display = "inline"; }
1513 var label_elements = document.getElementsByClassName("label-element");
1514 for(var i=0; i<label_elements.length; i++) { label_elements[i].style.display = "none"; }
1517 function saveLabelDesign() {
1518 var label_elements = document.getElementsByClassName('label-element');
1519 label_elements = Array.prototype.slice.call(label_elements); // convert to array
1520 if (label_elements.length < 1) {
1521 alert("No elements in the design. Please add design elements before saving");
1525 var lo = new CXGN.List();
1526 var new_name = $('#save_design_name').val();
1527 page_params = JSON.stringify(retrievePageParams());
1529 if (!page_params) {return;}
1530 var data = page_params.slice(1, -1).split(",").join("\n"); // get key value pairs in list format
1531 label_params = label_elements.filter(checkIfVisible).map(getLabelDetails);
1533 for (i=0; i < label_params.length; i++) { // add numbered element key for each set of label params
1534 data += '\n"element'+i+'": '+JSON.stringify(label_params[i]);
1537 list_id = lo.newList(new_name);
1539 var elementsAdded = lo.addToList(list_id, data);
1540 lo.setListType(list_id, 'label_design');
1542 if (elementsAdded) {
1543 alert("Saved label design with name " + new_name);
1547 function fillInPlaceholders(match, placeholder) { // replace placeholders with actual values
1548 var filled = add_fields[placeholder];
1549 // console.log("Filling "+placeholder+" with "+filled);
1550 if (typeof filled === 'undefined' && !placeholder.match(/Number:/)) {
1551 // console.log(placeholder+" is undefined. Alerting with warning");
1552 alert("Missing field. Your selected design includes the field "+placeholder+" which is not available from the selected data source. Please pick a different saved design or data source, or remove the undefined field from the design area.")
1557 function showLoadOption() {
1558 // document.getElementById('design_label').style.display = "inline";
1559 // document.getElementById('design_list').style.display = "inline";
1560 var lo = new CXGN.List();
1561 $('#design_list').html(lo.listSelect('design_list', ['label_design'], 'Select a saved design', 'refresh', undefined));
1562 $('#design_list_list_select').change(
1564 Workflow.complete(this);
1565 loadDesign(this.value);
1566 jQuery('#design_label_button').prop('disabled', false);
1570 function loadDesign (list_id) {
1571 // console.log("Loading design from list with ID "+list_id);
1572 // clear existing draw area
1573 initializeDrawArea();
1575 var lo = new CXGN.List();
1576 var list_data = lo.getListData(list_id);
1577 var elements = list_data.elements;
1579 //parse into javascript object
1580 var fixed_elements = Object.values(elements).map(function(e){ return e.pop(); });
1581 var params = JSON.parse("{"+fixed_elements.join(',')+"}");
1583 for (var key in params) {
1584 if (key.match(/element/)) {
1585 var element_obj = params[key];
1586 var value = element_obj.value;
1587 var text = value.replace(/\{(.*?)\}/g, fillInPlaceholders);
1588 //console.log("Width is "+element_obj.width);
1589 addToLabel(value, text, element_obj.type, element_obj.size, element_obj.font, element_obj.x, element_obj.y, element_obj.width, element_obj.height);
1593 var page = params['page_format'];
1594 document.getElementById('page_format').value = page;
1595 //console.log("page is "+page);
1596 switchPageDependentOptions(page);
1597 if (page == 'Custom') {
1598 document.getElementById("page_width").value = params['page_width'];
1599 document.getElementById("page_height").value = params['page_height'];
1602 var label = params['label_format'];
1603 document.getElementById('label_format').value = label;
1604 switchLabelDependentOptions(label);
1605 if (label == 'Custom') {
1606 document.getElementById("label_width").value = params['label_width'];
1607 document.getElementById("label_height").value = params['label_height'];
1608 page_formats[page].label_sizes['Custom'].label_width = params['label_width'];
1609 page_formats[page].label_sizes['Custom'].label_height = params['label_height'];
1610 changeLabelSize(params['label_width'], params['label_height']);
1611 $("#d3-add-and-download-div").removeAttr('style');
1615 saveAdditionalOptions(
1616 params['top_margin'],
1617 params['left_margin'],
1618 params['horizontal_gap'],
1619 params['vertical_gap'],
1620 params['number_of_columns'],
1621 params['number_of_rows'],
1622 params['plot_filter'],
1623 params['sort_order_1'],
1624 params['sort_order_2'],
1625 params['sort_order_3'],
1626 params['copies_per_plot']