<commit_before>var Camera = {
  _filter: '',
  _camera: 0,

  get video() {
    return document.getElementById('video');
  },

  init: function cameraInit() {
    var width, height;
    if (window.innerWidth > window.innerHeight) {
      width = window.innerWidth;
      height = window.innerHeight;
    } else {
      width = window.innerHeight;
      height = window.innerWidth;

      var deltaX = (width - height) / 2;
      var transform = 'rotate(90deg)';
      if (this._camera == 1)
        transform += ' scale(-1, 1)';

      var container = document.getElementById('video-container');
      container.style.MozTransform = transform;
    }

    var config = {
      width: width,
      height: height,
      camera: this._camera
    }

    var video = this.video;
    video.style.width = width + 'px';
    video.style.height = height + 'px';
    video.style.filter = this._filter;

    video.src = navigator.mozCamera.getCameraURI(config);
  },

  toggleCamera: function toggleCamera() {
    this._camera = 1 - this._camera;
    this.video.src = '';
    this.init();
  },

  toggleFilter: function toggleFilter(target) {
    var filter = '';

    if (!target.classList.contains('selected')) {
      var childs = target.parentNode.childNodes;
      for (var i = 0; i < childs.length; i++) {
        var child = childs[i];
        if (child.classList)
          child.classList.remove('selected');
      }

      filter = 'url(#' + target.dataset.filter + ')';
    }

    this._filter = this.video.style.filter = filter;
    target.classList.toggle('selected');
  }
};

window.addEventListener('DOMContentLoaded', function CameraInit() {
  Camera.init();
});

<commit_msg>Pause the camera when the app is minimized<commit_after>var Camera = {
  _filter: '',
  _camera: 0,

  get video() {
    return document.getElementById('video');
  },

  init: function cameraInit() {
    var width, height;
    if (window.innerWidth > window.innerHeight) {
      width = window.innerWidth;
      height = window.innerHeight;
    } else {
      width = window.innerHeight;
      height = window.innerWidth;

      var deltaX = (width - height) / 2;
      var transform = 'rotate(90deg)';
      if (this._camera == 1)
        transform += ' scale(-1, 1)';

      var container = document.getElementById('video-container');
      container.style.MozTransform = transform;
    }

    var config = {
      width: width,
      height: height,
      camera: this._camera
    }

    var video = this.video;
    video.style.width = width + 'px';
    video.style.height = height + 'px';
    video.style.filter = this._filter;

    video.src = navigator.mozCamera.getCameraURI(config);
  },

  pause: function pause() {
    this.video.pause();
  },

  toggleCamera: function toggleCamera() {
    this._camera = 1 - this._camera;
    this.video.src = '';
    this.init();
  },

  toggleFilter: function toggleFilter(target) {
    var filter = '';

    if (!target.classList.contains('selected')) {
      var childs = target.parentNode.childNodes;
      for (var i = 0; i < childs.length; i++) {
        var child = childs[i];
        if (child.classList)
          child.classList.remove('selected');
      }

      filter = 'url(#' + target.dataset.filter + ')';
    }

    this._filter = this.video.style.filter = filter;
    target.classList.toggle('selected');
  }
};

window.addEventListener('DOMContentLoaded', function CameraInit() {
  Camera.init();
});

// Bug 690056 implement a visibility API, and it's likely that
// we want this event to be fire when an app come back to life
// or is minimized (it does not now).
window.addEventListener('message', function CameraPause(evt) {
  if (evt.data.hidden) {
    Camera.pause();
  } else {
    Camera.init();
  }
});
<|endoftext|>


<commit_before>import { configure } from '@storybook/react'

const req = require.context('../packages', true, /\.stories\.ts$/)

function loadStories() {
  req.keys().forEach(req)
}

configure(loadStories, module)
<commit_msg>Load JS stories<commit_after>import { configure } from '@storybook/react'

const req = require.context('../packages/', true, /\.stories\.js$/)

function loadStories() {
  req.keys().forEach(req)
}

configure(loadStories, module)
<|endoftext|>


<commit_before>/**
 * The EvalSystem set of functions (keeps the namespace clean and tidy) -AZ
 * DEPENDS on jQuery 1.2 to operate
 * @author Aaron Zeckoski (aaronz@vt.edu)
 * @author Antranig Basman - dropbox methods at end
 */
var EvalSystem = function() {
	var $ = $ || function() { throw "JQuery undefined"; };
	var RSF = RSF || function() { throw "RSF JS undefined"; };
	function $it(elementID) {
		return document.getElementById(elementID);
	}

	/**
	 * Because JQuery uses this sort of CSS based selector system, if you have
	 * wacky characters in your tag ID (such as RSF Colons) you have to escape
	 * them. At the moment this just escapes the colons, should probably have a
	 * fuller (or automatic) jquery escaping mechanism.
	 */
	function escIdForJquery(value) {
		var escaped = (value !== null ? "#" + value.replace(/:/g, "\\:") : null);
		escaped = (escaped === "#" ? null : escaped);
		return escaped;
	}

	/**
	 * Creates an adhoc group delete function for a specific link,
	 * this expects to be called by an event handler
	 */
	function makeAdhocDeleteFunction(deleteLink, adhocArea) {
		return function(event) {
			var url = deleteLink.attr("href");
			$.ajax({
				url: url,
				type: "DELETE",
				dataType: "text",
				cache: false,
				success: function(data, msg) {
				adhocArea.fadeOut(500, function() {
					adhocArea.hide(0);
					adhocArea.remove();
				});
			},
			error: function(request, msg, errorThrown) {
				var status = request.status;
				// 404 just means it does not exist which is ok
				if (status !== "404") {
					alert( "Failure: " + status + " :" + msg + " :" + errorThrown + " :" + XMLHttpRequest );
				}
			}
			});
			return false;
		};
	}

	return {

		/**
		 *  A little bit of javascript to validate the Eval Assign Page. At the
		 *  moment this is only for making sure users have selected at least one
		 *  group or hierarchy node or something.
		 *
		 * @errorDivId A Div with an already translated message we can light up.
		 * @submitButtonId The button that is doing the submitting.
		 */
		initEvalAssignValidation: function(formId, errorDivId, submitButtonId) {
			var form = $(escIdForJquery(formId));
			var errorDiv = $(escIdForJquery(errorDivId));
			var submitButton = $(escIdForJquery(submitButtonId));

			// Boolean to store whether the form should submit based on validation.
			var passes = false;

			form.submit(function() {
				if (!passes) {
					errorDiv.show();
					RSF.getDOMModifyFirer().fireEvent();
				}
				return passes;
			});

			var onAssignClick = function(event) {
				passes = false;
				var n = $(".evalgroupselect:checked").length;
				if (n > 0) {
					passes=true;
				}
			};

			submitButton.click( function(event) { onAssignClick(event); } );
		},

		/**
		 * Handle the setup of all adhoc group delete handlers
		 */
		initEvalAssignAdhocDelete: function(adhocAreaIds) {
			// get all adhoc areas (these will be hidden when the group is deleted)
			for (var i = 0; i < adhocAreaIds.length; i++) {
				var adhocAreaId = adhocAreaIds[i];
				var adhocArea = $(escIdForJquery(adhocAreaId));
				var deleteLink = $(escIdForJquery(adhocAreaId + "deleteGroupLink"));
				if (adhocArea.length > 0 && deleteLink.length > 0) {
					deleteLink.show(0); // only show the links which actually could be found
					var onAdhocDeleteClick = makeAdhocDeleteFunction(deleteLink, adhocArea);
					deleteLink.click( onAdhocDeleteClick );
				} else {
					// failed to find the id given, nothing really to do here
					//alert("Failed to find adhoc elements: " + adhocAreaId + " :" + adhocArea + " :" + deleteLink);
				}
			}
		},

		initEvalReportView: function (allCommentsId, allTextResponsesId) {
			// for now this is just enabling toggling of comments and responses
			// see the makeToggle method for description of arguments
			if (allCommentsId.length > 0) {
				$(escIdForJquery(allCommentsId)).show();
				EvalSystem.makeToggle(allCommentsId + "Show", allCommentsId + "Hide", null, "showcomments", false);
			}
			if (allTextResponsesId.length > 0) {
				$(escIdForJquery(allTextResponsesId)).show();
				EvalSystem.makeToggle(allTextResponsesId + "Show", allTextResponsesId + "Hide", null, "showtextresponses", false);
			}
		},

		/**
		 * Adds a limitation of numbers only to an input box (inputId),
		 * displays the numbers only warning in the message area (msgId)
		 */
		addNumericOnly: function(inputId, msgId) {
			var inputBox = $(escIdForJquery(inputId));
			var msgSpan = $(escIdForJquery(msgId));
			inputBox.keypress(function (e) {
				//if the letter is not digit then display error and don't type anything
				if( e.which !== 13 && e.which !== 8 && e.which !== 0 && (e.which < 48 || e.which > 57)) {
					//display error message
					msgSpan.html("Numbers Only").show().fadeOut(3000);
					return false;
				}
			});
		},

		/**
		 * This will hide an area (or a set areas that match the toggleClass)
		 * when the element with hideId is clicked (it will also be hidden),
		 * it will show the area(s) and the hideId element when the element with showId is clicked,
		 * and also hide the showId element
		 * showId: the id of the element that triggers the show when clicked
		 * hideId: the id of the element that triggers the hide when clicked
		 * areaId: the id of area (div probably) to show/hide, set null if not used
		 * toggleClass: a classname such that all elements with this class will be shown/hidden, set null if not used
		 * initialHide: if true then start things hidden, else start things shown (default if unspecified)
		 */
		makeToggle: function (showId, hideId, areaId, toggleClass, initialHide) {
			var showButton = $(escIdForJquery(showId));
			var hideButton = $(escIdForJquery(hideId));
			var area = $(escIdForJquery(areaId));
			var toggles = $(toggleClass === null ? null : "." + toggleClass);

			var togglearea = showButton.parent().parent();

			showButton.show();

			hideButton.show();
			showButton.parent().next().wrap("");

			var showAction = function(event) {
				showButton.hide();
				hideButton.show();
				area.show("normal");
				toggles.show("normal");
				RSF.getDOMModifyFirer().fireEvent();
			};

			var hideAction = function(event) {
				showButton.show();
				hideButton.hide();
				area.hide("normal");
				toggles.hide("normal");
				RSF.getDOMModifyFirer().fireEvent();
			};

			if (initialHide) {
				hideAction();
			} else {
				showAction();
			}

			var toggle = function(event) {
				showButton.toggle();
				hideButton.toggle();
				area.toggle("normal");
				toggles.toggle("normal");
				RSF.getDOMModifyFirer().fireEvent();
			};

			togglearea.click( function (event) { toggle(event); } );

			/*showButton.click( function (event) { showAction(event) });
	        hideButton.click( function (event) { hideAction(event) });*/
		},

		// this just makes it easier to add in more stuff later -AZ
		initEvalSettings: function (areaId, selectId, selectValue, reminderId) {
			// see the toggleAndDisableSetReminder method for description of arguments
			EvalSystem.toggleAndDisableSetReminder(areaId, selectId, selectValue, reminderId);
		},

		/**
		 * This will hide and disable the setReminder option area
		 * and force the setting to 0 reminders on the evaluation settings page
		 * when the anonymous auth option is selected OR reveal the option area
		 * areaId: the id of area (div probably) to show/hide
		 * selectId: the id of the select that triggers the show/hide
		 * selectValue: the value of the select that causes the hide action (other cause show)
		 * reminderId: the id of the form control to set when hide action happens
		 */
		toggleAndDisableSetReminder: function (areaId, selectId, selectValue, reminderId) {
			var area = $("#"+areaId); //document.getElementById(areaId);
			var select = $("#"+selectId); //document.getElementById(selectId);
			var reminder = $("#"+reminderId); //document.getElementById(reminderId);

			var changeAction = function(event) {
				var selectedValue = event.target.value;
				if (selectedValue === selectValue) {
					// values are the same so we need to hide things
					// only take action if needed so we are not resetting the field constantly
					if ( area.is(':visible') ) {
						// area is currently visible so hide it
						area.hide("normal");
						// force reminder to the no reminders setting of 0
						reminder.val('0');
					}
				} else {
					// set to one of the non-hiding options
					if ( area.is(':hidden') ) {
						// area is currently hidden so reveal it
						area.show("slow");
					}
				}
			};
			// add the event for onchange to trigger the changeAction method and cause it to fire immediately
			select.change( function(event) { changeAction(event); }).change();
		},

		getRelativeID: function (baseid, targetid) {
			var colpos = baseid.lastIndexOf(':');
			return baseid.substring(0, colpos + 1) + targetid;
		},

		// See http://www.pageresource.com/jscript/jdropbox.htm for general approach
		operateSelectLink: function(linkid, localselectid) {
			var selectid = EvalSystem.getRelativeID(linkid, localselectid);
			var selection = $it(selectid);
			var url = selection.options[selection.selectedIndex].value;
			$.facebox({ ajax: url });
		},

		decorateReorderSelects: function(namebase, count) {
			var makeButtonClick = function(b) {
				return function() {
					b.click();
				};
			};
			var buttonid = namebase + "hiddenBtn";
			var button = $it(buttonid);
			for (var i = 0; i < count; ++ i) {
				var selectid = namebase + "item-row:"+i+":item-select-selection";
				var selection = $it(selectid);
				selection.onchange = makeButtonClick(button);
			}
		}

	};
}();<commit_msg>EVALSYS-660. Fix facebox JS visibility in namespace<commit_after>/**
 * The EvalSystem set of functions (keeps the namespace clean and tidy) -AZ
 * DEPENDS on jQuery 1.2 to operate
 * @author Aaron Zeckoski (aaronz@vt.edu)
 * @author Antranig Basman - dropbox methods at end
 */
var EvalSystem = function() {
	var $ = $ || function() { throw "JQuery undefined"; };
	var RSF = RSF || function() { throw "RSF JS undefined"; };
	function $it(elementID) {
		return document.getElementById(elementID);
	}

	/**
	 * Because JQuery uses this sort of CSS based selector system, if you have
	 * wacky characters in your tag ID (such as RSF Colons) you have to escape
	 * them. At the moment this just escapes the colons, should probably have a
	 * fuller (or automatic) jquery escaping mechanism.
	 */
	function escIdForJquery(value) {
		var escaped = (value !== null ? "#" + value.replace(/:/g, "\\:") : null);
		escaped = (escaped === "#" ? null : escaped);
		return escaped;
	}

	/**
	 * Creates an adhoc group delete function for a specific link,
	 * this expects to be called by an event handler
	 */
	function makeAdhocDeleteFunction(deleteLink, adhocArea) {
		return function(event) {
			var url = deleteLink.attr("href");
			$.ajax({
				url: url,
				type: "DELETE",
				dataType: "text",
				cache: false,
				success: function(data, msg) {
				adhocArea.fadeOut(500, function() {
					adhocArea.hide(0);
					adhocArea.remove();
				});
			},
			error: function(request, msg, errorThrown) {
				var status = request.status;
				// 404 just means it does not exist which is ok
				if (status !== "404") {
					alert( "Failure: " + status + " :" + msg + " :" + errorThrown + " :" + XMLHttpRequest );
				}
			}
			});
			return false;
		};
	}

	return {

		/**
		 *  A little bit of javascript to validate the Eval Assign Page. At the
		 *  moment this is only for making sure users have selected at least one
		 *  group or hierarchy node or something.
		 *
		 * @errorDivId A Div with an already translated message we can light up.
		 * @submitButtonId The button that is doing the submitting.
		 */
		initEvalAssignValidation: function(formId, errorDivId, submitButtonId) {
			var form = $(escIdForJquery(formId));
			var errorDiv = $(escIdForJquery(errorDivId));
			var submitButton = $(escIdForJquery(submitButtonId));

			// Boolean to store whether the form should submit based on validation.
			var passes = false;

			form.submit(function() {
				if (!passes) {
					errorDiv.show();
					RSF.getDOMModifyFirer().fireEvent();
				}
				return passes;
			});

			var onAssignClick = function(event) {
				passes = false;
				var n = $(".evalgroupselect:checked").length;
				if (n > 0) {
					passes=true;
				}
			};

			submitButton.click( function(event) { onAssignClick(event); } );
		},

		/**
		 * Handle the setup of all adhoc group delete handlers
		 */
		initEvalAssignAdhocDelete: function(adhocAreaIds) {
			// get all adhoc areas (these will be hidden when the group is deleted)
			for (var i = 0; i < adhocAreaIds.length; i++) {
				var adhocAreaId = adhocAreaIds[i];
				var adhocArea = $(escIdForJquery(adhocAreaId));
				var deleteLink = $(escIdForJquery(adhocAreaId + "deleteGroupLink"));
				if (adhocArea.length > 0 && deleteLink.length > 0) {
					deleteLink.show(0); // only show the links which actually could be found
					var onAdhocDeleteClick = makeAdhocDeleteFunction(deleteLink, adhocArea);
					deleteLink.click( onAdhocDeleteClick );
				} else {
					// failed to find the id given, nothing really to do here
					//alert("Failed to find adhoc elements: " + adhocAreaId + " :" + adhocArea + " :" + deleteLink);
				}
			}
		},

		initEvalReportView: function (allCommentsId, allTextResponsesId) {
			// for now this is just enabling toggling of comments and responses
			// see the makeToggle method for description of arguments
			if (allCommentsId.length > 0) {
				$(escIdForJquery(allCommentsId)).show();
				EvalSystem.makeToggle(allCommentsId + "Show", allCommentsId + "Hide", null, "showcomments", false);
			}
			if (allTextResponsesId.length > 0) {
				$(escIdForJquery(allTextResponsesId)).show();
				EvalSystem.makeToggle(allTextResponsesId + "Show", allTextResponsesId + "Hide", null, "showtextresponses", false);
			}
		},

		/**
		 * Adds a limitation of numbers only to an input box (inputId),
		 * displays the numbers only warning in the message area (msgId)
		 */
		addNumericOnly: function(inputId, msgId) {
			var inputBox = $(escIdForJquery(inputId));
			var msgSpan = $(escIdForJquery(msgId));
			inputBox.keypress(function (e) {
				//if the letter is not digit then display error and don't type anything
				if( e.which !== 13 && e.which !== 8 && e.which !== 0 && (e.which < 48 || e.which > 57)) {
					//display error message
					msgSpan.html("Numbers Only").show().fadeOut(3000);
					return false;
				}
			});
		},

		/**
		 * This will hide an area (or a set areas that match the toggleClass)
		 * when the element with hideId is clicked (it will also be hidden),
		 * it will show the area(s) and the hideId element when the element with showId is clicked,
		 * and also hide the showId element
		 * showId: the id of the element that triggers the show when clicked
		 * hideId: the id of the element that triggers the hide when clicked
		 * areaId: the id of area (div probably) to show/hide, set null if not used
		 * toggleClass: a classname such that all elements with this class will be shown/hidden, set null if not used
		 * initialHide: if true then start things hidden, else start things shown (default if unspecified)
		 */
		makeToggle: function (showId, hideId, areaId, toggleClass, initialHide) {
			var showButton = $(escIdForJquery(showId));
			var hideButton = $(escIdForJquery(hideId));
			var area = $(escIdForJquery(areaId));
			var toggles = $(toggleClass === null ? null : "." + toggleClass);

			var togglearea = showButton.parent().parent();

			showButton.show();

			hideButton.show();
			showButton.parent().next().wrap("");

			var showAction = function(event) {
				showButton.hide();
				hideButton.show();
				area.show("normal");
				toggles.show("normal");
				RSF.getDOMModifyFirer().fireEvent();
			};

			var hideAction = function(event) {
				showButton.show();
				hideButton.hide();
				area.hide("normal");
				toggles.hide("normal");
				RSF.getDOMModifyFirer().fireEvent();
			};

			if (initialHide) {
				hideAction();
			} else {
				showAction();
			}

			var toggle = function(event) {
				showButton.toggle();
				hideButton.toggle();
				area.toggle("normal");
				toggles.toggle("normal");
				RSF.getDOMModifyFirer().fireEvent();
			};

			togglearea.click( function (event) { toggle(event); } );

			/*showButton.click( function (event) { showAction(event) });
	        hideButton.click( function (event) { hideAction(event) });*/
		},

		// this just makes it easier to add in more stuff later -AZ
		initEvalSettings: function (areaId, selectId, selectValue, reminderId) {
			// see the toggleAndDisableSetReminder method for description of arguments
			EvalSystem.toggleAndDisableSetReminder(areaId, selectId, selectValue, reminderId);
		},

		/**
		 * This will hide and disable the setReminder option area
		 * and force the setting to 0 reminders on the evaluation settings page
		 * when the anonymous auth option is selected OR reveal the option area
		 * areaId: the id of area (div probably) to show/hide
		 * selectId: the id of the select that triggers the show/hide
		 * selectValue: the value of the select that causes the hide action (other cause show)
		 * reminderId: the id of the form control to set when hide action happens
		 */
		toggleAndDisableSetReminder: function (areaId, selectId, selectValue, reminderId) {
			var area = $("#"+areaId); //document.getElementById(areaId);
			var select = $("#"+selectId); //document.getElementById(selectId);
			var reminder = $("#"+reminderId); //document.getElementById(reminderId);

			var changeAction = function(event) {
				var selectedValue = event.target.value;
				if (selectedValue === selectValue) {
					// values are the same so we need to hide things
					// only take action if needed so we are not resetting the field constantly
					if ( area.is(':visible') ) {
						// area is currently visible so hide it
						area.hide("normal");
						// force reminder to the no reminders setting of 0
						reminder.val('0');
					}
				} else {
					// set to one of the non-hiding options
					if ( area.is(':hidden') ) {
						// area is currently hidden so reveal it
						area.show("slow");
					}
				}
			};
			// add the event for onchange to trigger the changeAction method and cause it to fire immediately
			select.change( function(event) { changeAction(event); }).change();
		},

		getRelativeID: function (baseid, targetid) {
			var colpos = baseid.lastIndexOf(':');
			return baseid.substring(0, colpos + 1) + targetid;
		},

		// See http://www.pageresource.com/jscript/jdropbox.htm for general approach
		operateSelectLink: function(linkid, localselectid) {
			var selectid = EvalSystem.getRelativeID(linkid, localselectid);
			var selection = $it(selectid);
			var url = selection.options[selection.selectedIndex].value;
			jQuery.facebox({ ajax: url });
		},

		decorateReorderSelects: function(namebase, count) {
			var makeButtonClick = function(b) {
				return function() {
					b.click();
				};
			};
			var buttonid = namebase + "hiddenBtn";
			var button = $it(buttonid);
			for (var i = 0; i < count; ++ i) {
				var selectid = namebase + "item-row:"+i+":item-select-selection";
				var selection = $it(selectid);
				selection.onchange = makeButtonClick(button);
			}
		}

	};
}();<|endoftext|>


<commit_before>const {
  transformToSaveDnBCompanyInvestigation,
  transformToCreateDnbCompanyInvestigation,
} = require('../transformers')

describe('Companies add company transformers', () => {
  describe('#transformToSaveDnBCompanyInvestigation', () => {
    context('when all fields are populated', () => {
      let actual

      beforeEach(() => {
        actual = transformToSaveDnBCompanyInvestigation({
          business_type: '1',
          name: 'name',
          website: 'website',
          telephone_number: '123',
          address1: 'line 1',
          address2: 'line 2',
          city: 'town',
          county: 'county',
          postcode: 'postcode',
          country: 'country',
          uk_region: '2',
          sector: '3',
        })
      })

      it('should transform the request body', () => {
        expect(actual).to.deep.equal({
          address: {
            country: {
              id: 'country',
            },
            county: 'county',
            line_1: 'line 1',
            line_2: 'line 2',
            postcode: 'postcode',
            town: 'town',
          },
          business_type: '1',
          name: 'name',
          sector: '3',
          telephone_number: '123',
          uk_region: '2',
          website: 'website',
        })
      })
    })

    context('when address line 2 and county are null', () => {
      let actual

      beforeEach(() => {
        actual = transformToSaveDnBCompanyInvestigation({
          business_type: '1',
          name: 'name',
          website: 'website',
          telephone_number: '123',
          address1: 'line 1',
          address2: null,
          city: 'town',
          county: null,
          postcode: 'postcode',
          country: 'country',
          uk_region: '2',
          sector: '3',
        })
      })

      it('should transform the request body with empty strings', () => {
        expect(actual).to.deep.equal({
          address: {
            country: {
              id: 'country',
            },
            county: '',
            line_1: 'line 1',
            line_2: '',
            postcode: 'postcode',
            town: 'town',
          },
          business_type: '1',
          name: 'name',
          sector: '3',
          telephone_number: '123',
          uk_region: '2',
          website: 'website',
        })
      })
    })
  })

  describe('#transformToCreateDnbCompanyInvestigation', () => {
    context('when all fields are populated', () => {
      let actual

      beforeEach(() => {
        actual = transformToCreateDnbCompanyInvestigation(
          {
            name: 'name',
            website: 'website',
            telephone_number: '123',
            address1: 'line 1',
            address2: 'line 2',
            city: 'town',
            county: 'county',
            postcode: 'postcode',
            country: 'country',
          },
          '123'
        )
      })

      it('should transform the request body', () => {
        expect(actual).to.deep.equal({
          company: '123',
          name: 'name',
          website: 'website',
          telephone_number: '123',
          address: {
            line_1: 'line 1',
            line_2: 'line 2',
            town: 'town',
            county: 'county',
            postcode: 'postcode',
            country: 'country',
          },
        })
      })
    })
  })
})
<commit_msg>PR refinements<commit_after>const {
  transformToSaveDnBCompanyInvestigation,
  transformToCreateDnbCompanyInvestigation,
} = require('../transformers')

describe('Companies add company transformers', () => {
  describe('#transformToSaveDnBCompanyInvestigation', () => {
    context('when all fields are populated', () => {
      let actual

      beforeEach(() => {
        actual = transformToSaveDnBCompanyInvestigation({
          business_type: '1',
          name: 'name',
          website: 'website',
          telephone_number: '123',
          address1: 'line 1',
          address2: 'line 2',
          city: 'town',
          county: 'county',
          postcode: 'postcode',
          country: 'country',
          uk_region: '2',
          sector: '3',
        })
      })

      it('should transform the request body', () => {
        expect(actual).to.deep.equal({
          address: {
            country: {
              id: 'country',
            },
            county: 'county',
            line_1: 'line 1',
            line_2: 'line 2',
            postcode: 'postcode',
            town: 'town',
          },
          business_type: '1',
          name: 'name',
          sector: '3',
          telephone_number: '123',
          uk_region: '2',
          website: 'website',
        })
      })
    })

    context('when address line 2 and county are null', () => {
      let actual

      beforeEach(() => {
        actual = transformToSaveDnBCompanyInvestigation({
          business_type: '1',
          name: 'name',
          website: 'website',
          telephone_number: '123',
          address1: 'line 1',
          address2: null,
          city: 'town',
          county: null,
          postcode: 'postcode',
          country: 'country',
          uk_region: '2',
          sector: '3',
        })
      })

      it('should transform the request body with empty strings', () => {
        expect(actual).to.deep.equal({
          address: {
            country: {
              id: 'country',
            },
            county: '',
            line_1: 'line 1',
            line_2: '',
            postcode: 'postcode',
            town: 'town',
          },
          business_type: '1',
          name: 'name',
          sector: '3',
          telephone_number: '123',
          uk_region: '2',
          website: 'website',
        })
      })
    })
  })

  describe('#transformToCreateDnbCompanyInvestigation', () => {
    context('when all fields are populated', () => {
      let actual = transformToCreateDnbCompanyInvestigation(
        {
          name: 'name',
          website: 'website',
          telephone_number: '123',
          address1: 'line 1',
          address2: 'line 2',
          city: 'town',
          county: 'county',
          postcode: 'postcode',
          country: 'country',
        },
        '123'
      )

      it('should transform the request body', () => {
        expect(actual).to.deep.equal({
          company: '123',
          name: 'name',
          website: 'website',
          telephone_number: '123',
          address: {
            line_1: 'line 1',
            line_2: 'line 2',
            town: 'town',
            county: 'county',
            postcode: 'postcode',
            country: 'country',
          },
        })
      })
    })
  })
})
<|endoftext|>


<commit_before>import { configure, addDecorator } from '@storybook/react';
import { setDefaults } from '@storybook/addon-info';
import { setOptions } from '@storybook/addon-options';
import backgroundColor from 'react-storybook-decorator-background';

// addon-info
setDefaults({
  header: false,
  inline: false,
  source: true,
  propTablesExclude: [],
  styles: stylesheet => {
    stylesheet.link.topRight.zIndex = 99999999;
    return stylesheet;
  }
});

setOptions({
  name: `Version ${process.env.__VERSION__}`,
  url: 'https://teamleader.design'
});

addDecorator(backgroundColor(['#ffffff', '#000000']));

const req = require.context('../stories', true, /\.js$/);

configure(() => {
  req.keys().forEach(filename => req(filename));
}, module);
<commit_msg>Added allowed preview background colors<commit_after>import { configure, addDecorator } from '@storybook/react';
import { setDefaults } from '@storybook/addon-info';
import { setOptions } from '@storybook/addon-options';
import backgroundColor from 'react-storybook-decorator-background';

// addon-info
setDefaults({
  header: false,
  inline: false,
  source: true,
  propTablesExclude: [],
  styles: stylesheet => {
    stylesheet.link.topRight.zIndex = 99999999;
    return stylesheet;
  }
});

setOptions({
  name: `Version ${process.env.__VERSION__}`,
  url: 'https://teamleader.design'
});

addDecorator(backgroundColor(['#ffffff', '#e6f2ff', '#ffeecc', '#d3f3f3', '#ffe3d9', '#e1edfa', '#f1f0ff', '#344b63']));

const req = require.context('../stories', true, /\.js$/);

configure(() => {
  req.keys().forEach(filename => req(filename));
}, module);
<|endoftext|>


<commit_before>/*!
 * backbone.layoutmanager.js v0.1.1
 * Copyright 2011, Tim Branyen (@tbranyen)
 * backbone.layoutmanager.js may be freely distributed under the MIT license.
 */
(function(Backbone, _, $) {

// Appease jshint which is unable to understand function hoisting and claims
// these functions are undefined;
var LayoutManager, renderViews, wrappedRender;

// Allows the setting of multiple views instead of a single view
function setViews(views) {
  // Iterate over all the views and use the View's view method to assign.
  _.each(views, function(view, name) {
    // Assign each view
    this.view(name, view);
  }, this);
}

function viewMethod(name, subView) {
  // Maintain a reference to the manager
  var manager = this;
  // Shorthand options
  var options = _.extend({}, this.options, LayoutManager.prototype.options);

  // Returns an object that provides asynchronous capabilities.
  function async(done) {
    var handler = options.deferred();

    // Used to handle asynchronous renders
    handler.async = function() {
      handler._isAsync = true;

      return done;
    };

    // This is used internally for when to apply to a layout
    handler.partial = options.deferred();

    return handler;
  }

  // Passed to each View's render.  This function handles the wrapped
  // View and the call to render.
  function viewRender(view) {
    var url, handler, prefix, contents;

    // Once the template is successfully fetched, use its contents to
    // proceed.
    function templateDone(context, contents) {
      // Ensure the cache is up-to-date.
      LayoutManager.cache(url, contents);

      // Render the View into the el property.
      options.html(view.el, options.render(contents, context));

      // Resolve partials with the View element.
      handler.partial.resolve(view.el);

      // Render any additional views.
      renderViews(view, view.views);
    }

    // Reset the internal views array during every render.
    view.views = {};

    // Return the render method for View's to call.
    return {
      // Allows additional views to be inserted at render time.
      insert: function(partial, subView) {
        // Create or append to views object
        var views = view.views;

        // Create or append to partials array
        var viewPartial = views[partial] = views[partial] || [];

        // Push the subView into the stack of partials
        viewPartial.push(subView);

        // Add the reusable view method to all views added this way as well.
        subView.view = viewMethod;

        // Add the reusable bulk setViews method as well.
        subView.setViews = setViews;

        // Keep the chain going
        return subView;
      },

      // Render accepts an option context object.
      render: function(context) {
        // Seek out serialize method and use that object.
        if (!context && _.isFunction(view.serialize)) {
          context = view.serialize.call(view);
        // If serialize already is an object, just use that
        } else if (!context && _.isObject(view.serialize)) {
          context = view.serialize;
        }

        // Create an asynchronous handler.
        handler = async(_.bind(templateDone, manager, context));

        // Set the prefix
        prefix = options.paths && options.paths.template || "";

        // Set the url to the prefix + the view's template property.
        if (_.isString(view.template)) {
          url = prefix + view.template;
        }

        // Check if contents are already cached
        if (contents = LayoutManager.cache(url)) {
          // Make this function act asynchronous to avoid issues with event
          // binding and other unintentional consequences of different timing
          // from synchronous operations.
          templateDone(context, contents, url);

          return handler;
        }

        // Fetch layout and template contents
        if (_.isString(view.template)) {
          contents = options.fetch.call(handler, prefix + view.template);
        // If its not a string just pass the object/function/whatever
        } else {
          contents = options.fetch.call(handler, view.template);
        }

        // If the function was synchronous, continue execution.
        if (!handler._isAsync) {
          templateDone(context, contents);
        }

        return handler;
      }
    };
  }

  // Wraps the View's original render to supply a reusable render method
  function wrappedRender(root, name, view) {
    var hasRendered;
    var original = view.render;

    // This render method accepts no arguments and will simply update the
    // SubView from the rules provided inside the render method.
    return function() {
      // Render into a variable
      var viewDeferred = original.call(view, viewRender);

      // If the view has already been rendered, do not remove and re-add,
      // simply re-render.
      if (hasRendered) {
        // If the view contains a views object, iterate over it as well
        if (_.isObject(view.options.views)) {
          renderViews(view, view.options.views);
        }

        // This will be useful to allow wrapped renders to know when they are
        // done as well
        viewDeferred.partial.then(function(el) {
          return viewDeferred.resolve(el);
        });

        return viewDeferred;
      }

      // Internal partial deferred used for injecting into layout
      viewDeferred.partial.then(function(el) {
        // Apply partially
        options.partial(root.el, name, el, view.options.append);

        // Let us know the view has been rendered
        hasRendered = true;

        // Once added to the DOM resolve original deferred, with the correct
        // view element.
        viewDeferred.resolve(view.el);

        // If the view contains a views object, iterate over it as well
        if (_.isObject(view.options.views)) {
          renderViews(view, view.options.views);
        }
      });

      // This will be useful to allow wrapped renders to know when they are
      // done as well
      return viewDeferred;
    };
  }

  // Recursively iterate over each View and apply the render method
  function renderViews(root, views) {
    // Take in a view and a name and perform mighty magic to ensure the
    // template is loaded and rendered.  Wraps in a new render method so
    // that you can call to update a single model.  May be optionally
    // asynchronous if the done callback is provided.
    function processView(view, name, done) {
      view.remove();

      // Wrap a new reusable render method, ensure that a wrapped flag is
      // set to prevent double wrapping.
      if (!view.render._wrapped) {
        view.render = wrappedRender(root, name, view);

        // This flag is used to determine which render method is being looked
        // at.
        view.render._wrapped = true;
      }

      // Render each View
      view.render().then(done).then(function() {
        view.delegateEvents();
      });
    }

    // For each view access the view object and partial name
    _.each(views, function(view, name) {
      // Take each subView and pipe it into the processView function
      function iterateViews(views) {
        // Remove the currentView from the views array and assign it
        // to be a SubView.
        var subView = views.shift();

        // Automatically convert lists to append
        subView.options.append = true;

        // Process the views serially
        processView(subView, name, function() {
          // Recurse to the next view
          if (views.length) {
            iterateViews(views);
          }
        });
      }

      // If the views is an array render out as a list
      if (_.isArray(view)) {
        iterateViews(_.clone(view));
      // Process a single view
      } else {
        processView(view, name);
      }
    });
  }

  // Determine if we are already dealing with a wrapped render function, if
  // so, do not attempt to re-wrap.
  if (!subView.render._wrapped) {
    subView.render = wrappedRender(manager, name, subView);

    // This flag is used to determine which render method is being looked
    // at.
    subView.render._wrapped = true;
  }

  // Add the reusable view function reference to every view added this way.
  subView.view = viewMethod;
  // Add the reusable bulk setViews method as well.
  subView.setViews = setViews;

  return this.views[name] = subView;
}

// LayoutManager at its core is specifically a Backbone.View
LayoutManager = Backbone.LayoutManager = Backbone.View.extend({
  initialize: function() {
    var prefix, url;
    // Handle views support
    var views = {};
    // Maintain a reference to the manager
    var manager = this;

    // Mix in the views function
    if (_.isFunction(this.options.views)) {
      _.extend(views, this.options.views.call(this));
      delete this.options.views;
    // Mix in the views object
    } else if (_.isObject(this.options.views)) {
      _.extend(views, this.options.views);
      delete this.options.views;
    }

    // Assign the new views object
    this.views = {};

    // Assign each sub View into the manager
    _.each(views, function(view, name) {
      manager.view(name, view);
    });

    // Merge in the default options
    this.options = _.extend({}, Backbone.LayoutManager, this.options);

    // Call any options intialize that may have been passed
    if (_.isFunction(this.options.initialize)) {
      this.options.initialize.apply(this, arguments);
    }

    // Ensure no context issues internally
    _.bindAll(this);

    // If there is no template or serialize property supplied attempt to pull
    // off instance, this should allow for extending easier.
    _.each(["template", "serialize"], function(prop) {
      if (!this.options[prop] && this[prop]) {
        this.options[prop] = this[prop];
      }
    }, this);

    // If events exist rip off and place on layout view
    if (this.options.events) {
      // Assign to top level so delegateEvents will work as expected
      this.events = this.options.events;
      // Delete off options
      delete this.options.events;

      _.each(this.events, function(method) {
        this[method] = this.options[method];

        // Delete method off options
        delete this.options[method];
      }, this);

      // Ensure events are bound on the layout.
      this.delegateEvents();
    }
  },

  // Provided to a top level layout to allow direct assignment of a SubView.
  view: viewMethod,

  // This allows a bulk replacement of all existing views
  setViews: setViews,

  render: function(done) {
    var contents, prefix, url, handler;
    var manager = this;
    var options = this.options;

    // Returns an object that provides asynchronous capabilities.
    function async(done) {
      var handler = options.deferred();

      // Used to handle asynchronous renders
      handler.async = function() {
        handler._isAsync = true;

        return done;
      };

      // This is used internally for when to apply to a layout
      handler.partial = options.deferred();

      return handler;
    }

    // Once the layout is successfully fetched, use its contents to proceed.
    function layoutDone(contents) {
      // Empty object if context is not provided
      var context = {};

      // Ensure the cache is up-to-date
      LayoutManager.cache(url, contents);

      // Context is a function
      if (_.isFunction(options.serialize)) {
        context = options.serialize.call(manager);
      // Context is an object
      } else if (_.isObject(options.serialize)) {
        context = options.serialize;
      }

      // Set the layout
      options.html(manager.el, options.render(contents, context));

      $(manager.el).detach();

      // Render the top-level views from the LayoutManager
      _.each(manager.views, function(view) {
        view.render();
      });

      // Call the original LayoutManager render method callback, with the
      // DOM element containing the layout and sub views.
      done(manager.el);
    }

    // This is essentially the pathing prefix.
    prefix = options.paths && options.paths.layout || "";

    // Set the url to the prefix + the layouts template property.
    if (_.isString(options.template)) {
      url = prefix + options.template;
    // If the template is not a string, don't prepend the prefix
    } else {
      url = options.template;
    }

    // Check if contents are already cached
    if (contents = LayoutManager.cache(url)) {
      return layoutDone(contents);
    }

    // Get layout contents
    handler = async(layoutDone);
    contents = options.fetch.call(handler, url);

    // If the function was synchronous, continue execution.
    if (!handler._isAsync) {
      layoutDone(contents);
    }
  }
},
{
  // Clearable cache
  _cache: {},

  // Cache templates into LayoutManager._cache
  // @path     : View's template property.
  // @contents : Template content's to cache.
  cache: function(path, contents) {
    // If template path is found in the cache, return the contents.
    if (path in this._cache) {
      return this._cache[path];
    // Ensure path and contents aren't undefined
    } else if (path != null && contents != null) {
      return this._cache[path] = contents;
    }

    // If template is not in the cache, return undefined.
  },

  // This static method allows for global configuration of LayoutManager.
  configure: function(opts) {
    var options = Backbone.LayoutManager.prototype.options;

    // Without this check the application would react strangely to a foreign
    // input.
    if (_.isObject(opts)) {
      _.extend(options, opts);
    }
  },

  View: Backbone.View.extend({
    // Render is given a callback layout which internally wraps the view
    // and has a render function that is callable. which you can pass an
    // object to.
    render: function(layout) {
      return layout(this).render();
    }
  })
});

// Default configuration options; designed to be overriden.
Backbone.LayoutManager.prototype.options = {

  // Layout and template properties can be assigned here to prefix
  // template/layout names.
  paths: {},

  // Can be used to supply a different deferred that implements Promises/A.
  deferred: function() {
    return $.Deferred();
  },

  // Fetch is passed a path and is expected to return template contents as a
  // string.
  fetch: function(path) {
    return $(path).html();
  },

  // This is really the only way you will want to partially apply a view into
  // a layout.  Its entirely possible you'll want to do it differently, so
  // this method is available to change.
  //
  // layout   : Is the LayoutManager's el property.
  // name     : Is the key name specified in the view assignment.
  // template : Is the View's el property.
  // append   : Should the view be appended?
  partial: function(layout, name, template, append) {
    if (append) {
      this.append($(layout).find(name), template);
    } else {
      this.html($(layout).find(name), template);
    }
  },

  // Override this with a custom HTML method, passed a root element and an
  // element to replace the innerHTML with.
  html: function(root, el) {
    $(root).html(el);
  },

  // Very similar to HTML except this one will appendChild.
  append: function(root, el) {
    $(root).append(el);
  },

  // By default, render using underscore's templating.
  render: function(template, context) {
    return _.template(template)(context);
  }

};

})(this.Backbone, this._, this.jQuery);
<commit_msg>made detach overridable<commit_after>/*!
 * backbone.layoutmanager.js v0.1.1
 * Copyright 2011, Tim Branyen (@tbranyen)
 * backbone.layoutmanager.js may be freely distributed under the MIT license.
 */
(function(Backbone, _, $) {

// Appease jshint which is unable to understand function hoisting and claims
// these functions are undefined;
var LayoutManager, renderViews, wrappedRender;

// Allows the setting of multiple views instead of a single view
function setViews(views) {
  // Iterate over all the views and use the View's view method to assign.
  _.each(views, function(view, name) {
    // Assign each view
    this.view(name, view);
  }, this);
}

function viewMethod(name, subView) {
  // Maintain a reference to the manager
  var manager = this;
  // Shorthand options
  var options = _.extend({}, this.options, LayoutManager.prototype.options);

  // Returns an object that provides asynchronous capabilities.
  function async(done) {
    var handler = options.deferred();

    // Used to handle asynchronous renders
    handler.async = function() {
      handler._isAsync = true;

      return done;
    };

    // This is used internally for when to apply to a layout
    handler.partial = options.deferred();

    return handler;
  }

  // Passed to each View's render.  This function handles the wrapped
  // View and the call to render.
  function viewRender(view) {
    var url, handler, prefix, contents;

    // Once the template is successfully fetched, use its contents to
    // proceed.
    function templateDone(context, contents) {
      // Ensure the cache is up-to-date.
      LayoutManager.cache(url, contents);

      // Render the View into the el property.
      options.html(view.el, options.render(contents, context));

      // Resolve partials with the View element.
      handler.partial.resolve(view.el);

      // Render any additional views.
      renderViews(view, view.views);
    }

    // Reset the internal views array during every render.
    view.views = {};

    // Return the render method for View's to call.
    return {
      // Allows additional views to be inserted at render time.
      insert: function(partial, subView) {
        // Create or append to views object
        var views = view.views;

        // Create or append to partials array
        var viewPartial = views[partial] = views[partial] || [];

        // Push the subView into the stack of partials
        viewPartial.push(subView);

        // Add the reusable view method to all views added this way as well.
        subView.view = viewMethod;

        // Add the reusable bulk setViews method as well.
        subView.setViews = setViews;

        // Keep the chain going
        return subView;
      },

      // Render accepts an option context object.
      render: function(context) {
        // Seek out serialize method and use that object.
        if (!context && _.isFunction(view.serialize)) {
          context = view.serialize.call(view);
        // If serialize already is an object, just use that
        } else if (!context && _.isObject(view.serialize)) {
          context = view.serialize;
        }

        // Create an asynchronous handler.
        handler = async(_.bind(templateDone, manager, context));

        // Set the prefix
        prefix = options.paths && options.paths.template || "";

        // Set the url to the prefix + the view's template property.
        if (_.isString(view.template)) {
          url = prefix + view.template;
        }

        // Check if contents are already cached
        if (contents = LayoutManager.cache(url)) {
          // Make this function act asynchronous to avoid issues with event
          // binding and other unintentional consequences of different timing
          // from synchronous operations.
          templateDone(context, contents, url);

          return handler;
        }

        // Fetch layout and template contents
        if (_.isString(view.template)) {
          contents = options.fetch.call(handler, prefix + view.template);
        // If its not a string just pass the object/function/whatever
        } else {
          contents = options.fetch.call(handler, view.template);
        }

        // If the function was synchronous, continue execution.
        if (!handler._isAsync) {
          templateDone(context, contents);
        }

        return handler;
      }
    };
  }

  // Wraps the View's original render to supply a reusable render method
  function wrappedRender(root, name, view) {
    var hasRendered;
    var original = view.render;

    // This render method accepts no arguments and will simply update the
    // SubView from the rules provided inside the render method.
    return function() {
      // Render into a variable
      var viewDeferred = original.call(view, viewRender);

      // If the view has already been rendered, do not remove and re-add,
      // simply re-render.
      if (hasRendered) {
        // If the view contains a views object, iterate over it as well
        if (_.isObject(view.options.views)) {
          renderViews(view, view.options.views);
        }

        // This will be useful to allow wrapped renders to know when they are
        // done as well
        viewDeferred.partial.then(function(el) {
          return viewDeferred.resolve(el);
        });

        return viewDeferred;
      }

      // Internal partial deferred used for injecting into layout
      viewDeferred.partial.then(function(el) {
        // Apply partially
        options.partial(root.el, name, el, view.options.append);

        // Let us know the view has been rendered
        hasRendered = true;

        // Once added to the DOM resolve original deferred, with the correct
        // view element.
        viewDeferred.resolve(view.el);

        // If the view contains a views object, iterate over it as well
        if (_.isObject(view.options.views)) {
          renderViews(view, view.options.views);
        }
      });

      // This will be useful to allow wrapped renders to know when they are
      // done as well
      return viewDeferred;
    };
  }

  // Recursively iterate over each View and apply the render method
  function renderViews(root, views) {
    // Take in a view and a name and perform mighty magic to ensure the
    // template is loaded and rendered.  Wraps in a new render method so
    // that you can call to update a single model.  May be optionally
    // asynchronous if the done callback is provided.
    function processView(view, name, done) {
      view.remove();

      // Wrap a new reusable render method, ensure that a wrapped flag is
      // set to prevent double wrapping.
      if (!view.render._wrapped) {
        view.render = wrappedRender(root, name, view);

        // This flag is used to determine which render method is being looked
        // at.
        view.render._wrapped = true;
      }

      // Render each View
      view.render().then(done).then(function() {
        view.delegateEvents();
      });
    }

    // For each view access the view object and partial name
    _.each(views, function(view, name) {
      // Take each subView and pipe it into the processView function
      function iterateViews(views) {
        // Remove the currentView from the views array and assign it
        // to be a SubView.
        var subView = views.shift();

        // Automatically convert lists to append
        subView.options.append = true;

        // Process the views serially
        processView(subView, name, function() {
          // Recurse to the next view
          if (views.length) {
            iterateViews(views);
          }
        });
      }

      // If the views is an array render out as a list
      if (_.isArray(view)) {
        iterateViews(_.clone(view));
      // Process a single view
      } else {
        processView(view, name);
      }
    });
  }

  // Determine if we are already dealing with a wrapped render function, if
  // so, do not attempt to re-wrap.
  if (!subView.render._wrapped) {
    subView.render = wrappedRender(manager, name, subView);

    // This flag is used to determine which render method is being looked
    // at.
    subView.render._wrapped = true;
  }

  // Add the reusable view function reference to every view added this way.
  subView.view = viewMethod;
  // Add the reusable bulk setViews method as well.
  subView.setViews = setViews;

  return this.views[name] = subView;
}

// LayoutManager at its core is specifically a Backbone.View
LayoutManager = Backbone.LayoutManager = Backbone.View.extend({
  initialize: function() {
    var prefix, url;
    // Handle views support
    var views = {};
    // Maintain a reference to the manager
    var manager = this;

    // Mix in the views function
    if (_.isFunction(this.options.views)) {
      _.extend(views, this.options.views.call(this));
      delete this.options.views;
    // Mix in the views object
    } else if (_.isObject(this.options.views)) {
      _.extend(views, this.options.views);
      delete this.options.views;
    }

    // Assign the new views object
    this.views = {};

    // Assign each sub View into the manager
    _.each(views, function(view, name) {
      manager.view(name, view);
    });

    // Merge in the default options
    this.options = _.extend({}, Backbone.LayoutManager, this.options);

    // Call any options intialize that may have been passed
    if (_.isFunction(this.options.initialize)) {
      this.options.initialize.apply(this, arguments);
    }

    // Ensure no context issues internally
    _.bindAll(this);

    // If there is no template or serialize property supplied attempt to pull
    // off instance, this should allow for extending easier.
    _.each(["template", "serialize"], function(prop) {
      if (!this.options[prop] && this[prop]) {
        this.options[prop] = this[prop];
      }
    }, this);

    // If events exist rip off and place on layout view
    if (this.options.events) {
      // Assign to top level so delegateEvents will work as expected
      this.events = this.options.events;
      // Delete off options
      delete this.options.events;

      _.each(this.events, function(method) {
        this[method] = this.options[method];

        // Delete method off options
        delete this.options[method];
      }, this);

      // Ensure events are bound on the layout.
      this.delegateEvents();
    }
  },

  // Provided to a top level layout to allow direct assignment of a SubView.
  view: viewMethod,

  // This allows a bulk replacement of all existing views
  setViews: setViews,

  render: function(done) {
    var contents, prefix, url, handler;
    var manager = this;
    var options = this.options;

    // Returns an object that provides asynchronous capabilities.
    function async(done) {
      var handler = options.deferred();

      // Used to handle asynchronous renders
      handler.async = function() {
        handler._isAsync = true;

        return done;
      };

      // This is used internally for when to apply to a layout
      handler.partial = options.deferred();

      return handler;
    }

    // Once the layout is successfully fetched, use its contents to proceed.
    function layoutDone(contents) {
      // Empty object if context is not provided
      var context = {};

      // Ensure the cache is up-to-date
      LayoutManager.cache(url, contents);

      // Context is a function
      if (_.isFunction(options.serialize)) {
        context = options.serialize.call(manager);
      // Context is an object
      } else if (_.isObject(options.serialize)) {
        context = options.serialize;
      }

      // Set the layout
      options.html(manager.el, options.render(contents, context));

      // Removes the shared element from the DOM before injection, this
      // prevents events from being removed by $.fn.cleanData.
      options.detach(manager.el);

      // Render the top-level views from the LayoutManager
      _.each(manager.views, function(view) {
        view.render();
      });

      // Call the original LayoutManager render method callback, with the
      // DOM element containing the layout and sub views.
      done(manager.el);
    }

    // This is essentially the pathing prefix.
    prefix = options.paths && options.paths.layout || "";

    // Set the url to the prefix + the layouts template property.
    if (_.isString(options.template)) {
      url = prefix + options.template;
    // If the template is not a string, don't prepend the prefix
    } else {
      url = options.template;
    }

    // Check if contents are already cached
    if (contents = LayoutManager.cache(url)) {
      return layoutDone(contents);
    }

    // Get layout contents
    handler = async(layoutDone);
    contents = options.fetch.call(handler, url);

    // If the function was synchronous, continue execution.
    if (!handler._isAsync) {
      layoutDone(contents);
    }
  }
},
{
  // Clearable cache
  _cache: {},

  // Cache templates into LayoutManager._cache
  // @path     : View's template property.
  // @contents : Template content's to cache.
  cache: function(path, contents) {
    // If template path is found in the cache, return the contents.
    if (path in this._cache) {
      return this._cache[path];
    // Ensure path and contents aren't undefined
    } else if (path != null && contents != null) {
      return this._cache[path] = contents;
    }

    // If template is not in the cache, return undefined.
  },

  // This static method allows for global configuration of LayoutManager.
  configure: function(opts) {
    var options = Backbone.LayoutManager.prototype.options;

    // Without this check the application would react strangely to a foreign
    // input.
    if (_.isObject(opts)) {
      _.extend(options, opts);
    }
  },

  View: Backbone.View.extend({
    // Render is given a callback layout which internally wraps the view
    // and has a render function that is callable. which you can pass an
    // object to.
    render: function(layout) {
      return layout(this).render();
    }
  })
});

// Default configuration options; designed to be overriden.
Backbone.LayoutManager.prototype.options = {

  // Layout and template properties can be assigned here to prefix
  // template/layout names.
  paths: {},

  // Can be used to supply a different deferred that implements Promises/A.
  deferred: function() {
    return $.Deferred();
  },

  // Fetch is passed a path and is expected to return template contents as a
  // string.
  fetch: function(path) {
    return $(path).html();
  },

  // This is really the only way you will want to partially apply a view into
  // a layout.  Its entirely possible you'll want to do it differently, so
  // this method is available to change.
  //
  // layout   : Is the LayoutManager's el property.
  // name     : Is the key name specified in the view assignment.
  // template : Is the View's el property.
  // append   : Should the view be appended?
  partial: function(layout, name, template, append) {
    if (append) {
      this.append($(layout).find(name), template);
    } else {
      this.html($(layout).find(name), template);
    }
  },

  // Override this with a custom HTML method, passed a root element and an
  // element to replace the innerHTML with.
  html: function(root, el) {
    $(root).html(el);
  },

  // Very similar to HTML except this one will appendChild.
  append: function(root, el) {
    $(root).append(el);
  },

  // Abstract out the $.fn.detach method
  detach: function(el) {
    $(el).detach();
  },

  // By default, render using underscore's templating.
  render: function(template, context) {
    return _.template(template)(context);
  }

};

})(this.Backbone, this._, this.jQuery);
<|endoftext|>