<commit_before>/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

/*global $, sdata, get_cookie, Config */

var sakai = sakai || {};

sakai.remotecontent = function(tuid, placement, showSettings){

	/////////////////////////////
	// Configuration variables //
	/////////////////////////////

	var rootel = $("#" + tuid);
	var json = false;
	var isAdvancedSettingsVisible = false;

	// Default values
	var defaultWidth = 100;
	var defaultWidthUnit = "%";
	var defaultHeight = 200;

	// Links and labels
	var remotecontent = "#remotecontent";
	var remotecontentSettings = remotecontent + "_settings";
	var remotecontentSettingsAdvanced = remotecontentSettings + "_advanced";
	var remotecontentSettingsAdvancedDown = remotecontentSettingsAdvanced + "_down";
	var remotecontentSettingsAdvancedToggleSettings = remotecontentSettingsAdvanced + "_toggle_settings";
	var remotecontentSettingsAdvancedUp = remotecontentSettingsAdvanced + "_up";
	var remotecontentSettingsBorders = remotecontentSettings + "_borders";
	var remotecontentSettingsCancel = remotecontentSettings + "_cancel";
	var remotecontentSettingsColorContainer = remotecontentSettings + "_color_container";
	var remotecontentSettingsHeight = remotecontentSettings + "_height";
	var remotecontentSettingsInsert = remotecontentSettings + "_insert";
	var remotecontentSettingsPreview = remotecontentSettings + "_preview";
	var remotecontentSettingsPreviewFrame = remotecontentSettingsPreview + "_frame";
	var remotecontentSettingsUrl = remotecontentSettings + "_url";
	var remotecontentSettingsWidth = remotecontentSettings + "_width";

	// Containers
	var remotecontentMainContainer = remotecontent + "_main_container";

	// Classes
	var remotecontentSettingsWidthUnitClass = ".remotecontent_settings_width_unit";
	var remotecontentSettingsWidthUnitSelectedClass = "remotecontent_settings_width_unit_selected";

	// Templates
	var remotecontentSettingsColorContainerTemplate = "remotecontent_settings_color_container_template";
	var remotecontentSettingsTemplate = "remotecontent_settings_template";
	var remotecontentSettingsPreviewTemplate = "remotecontent_settings_preview_template";

	///////////////////////
	// Utility functions //
	///////////////////////

	/**
	 * Check if the value is a decimal or not
	 * @param {Object} value Value that needs to be checked
	 * @return {Boolean}
	 * 	true: is a decimal
	 * 	false: is not a decimal
	 */
	var isDecimal = function(value){
		return (/^\d+$/).test(value);
	};

	/**
	 * Check if the input url is in fact an url or not
	 * @param {String} url Url that needs to be tested
	 * @return {Boolean}
	 * 	true: is an url
	 * 	false: is not an url
	 */
	var isUrl = function(url){
		return (/^http\:\/\/|^https\:\/\//i).test(url);
	};

	/**
	 * Called when the data has been saved to the JCR.
	 */
	var savedDataToJCR = function() {
		sdata.container.informFinish(tuid);
	};

	//////////////////////
	// Render functions //
	//////////////////////

	/**
	 * Render the iframe for the widget in settings mode
	 * @param {Boolean} complete Render the preview completely or only adjust values
	 */
	var renderIframeSettings = function(complete){
		if(complete){
			// We create this object to render the iframe with the default height, width and widthunit
			var jsonDefaultSize = {};
			jsonDefaultSize = json;
			jsonDefaultSize.width = defaultWidth;
			jsonDefaultSize.width_unit = defaultWidthUnit;
			jsonDefaultSize.height = defaultHeight;
			$(remotecontentSettingsPreview).html($.Template.render(remotecontentSettingsPreviewTemplate, json));
		}else{
			$(remotecontentSettingsPreviewFrame).attr("style", "border: " + json.border_size + "px #" + json.border_color + " solid");
		}
	};

	/**
	 * Render the iframe for the widget
	 */
	var renderIframe = function(){
		if(json){
			$(remotecontentMainContainer, rootel).html($.Template.render(remotecontentSettingsPreviewTemplate, json));
		}
	};

	/**
	 * Render the html of the remotecontentsettings
	 */
	var renderRemoteContentSettings = function() {
		if(json){
			$(remotecontentSettings).html($.Template.render(remotecontentSettingsTemplate, json));
		}
	};

	/**
	 * Render the color container
	 */
	var renderColorContainer = function(){
		if(json){
			$(remotecontentSettingsColorContainer).html($.Template.render(remotecontentSettingsColorContainerTemplate, json));
		}
	};

	//////////////////////
	// Global functions //
	//////////////////////

	/**
	 * Display the iframe in normal mode
	 * @param {Object} parameters JSON object that contains the necessary information for the iframe
	 */
	var displayRemoteContent = function(parameters) {
		json = parameters;
		renderIframe();
	};

	/**
	 * Save the remotecontent to the jcr
	 */
	var saveRemoteContent = function() {
		if (json.url !== "") {
			var str = $.toJSON(json); // Convert the posts to a JSON string
			var saveUrl = Config.URL.SDATA_FETCH_BASIC_URL.replace(/__PLACEMENT__/, placement).replace(/__TUID__/, tuid);
			sdata.widgets.WidgetPreference.save(saveUrl, "remotecontent", str, savedDataToJCR);
		}
	};

	/**
	 * Change the direction (up/down) of the arrow for the advanced settings
	 */
	var changeAdvancedSettingsArrow = function(){
		if (isAdvancedSettingsVisible) {
			$(remotecontentSettingsAdvancedDown, rootel).hide();
			$(remotecontentSettingsAdvancedUp, rootel).show();
		}
		else {
			$(remotecontentSettingsAdvancedUp, rootel).hide();
			$(remotecontentSettingsAdvancedDown, rootel).show();
		}
	};

	//////////////
	// Bindings //
	//////////////

	/*
	 * Add binding to the color boxes
	 */
	var addColorBinding = function(){
		$(".remotecontent_settings_color").click(function() {
			json.border_color = $(this).attr("id").split("_")[$(this).attr("id").split("_").length - 1];
			renderIframeSettings(false);
			renderColorContainer();
			addColorBinding();
		});
	};

	/*
	 * Add binding to all the elements
	 */
	var addBinding = function(){

		// Change the url for the iFrame
		$(remotecontentSettingsUrl).change(function() {
			var urlValue = $(this).val();
			if (urlValue !== "") {
				// Check if someone already wrote http inside the url
				if (!isUrl(urlValue)) {
					urlValue = 'http://' + urlValue;
				}
				json.url = urlValue;
				renderIframeSettings(true);
			}
		});

		// Change the iframe width
		$(remotecontentSettingsWidth).change(function() {
			var widthValue = $(remotecontentSettingsWidth).val();

			if(isDecimal(widthValue)){
				json.width = widthValue;
			}
			renderIframeSettings(false);
		});

		// Change the iframe height
		$(remotecontentSettingsHeight).change(function() {
			var heightValue = $(remotecontentSettingsHeight).val();

			if(isDecimal(heightValue)){
				json.height = heightValue;
			}
			renderIframeSettings(false);
		});

		// Change the border width
		$(remotecontentSettingsBorders).change(function() {
			var borderValue = $(remotecontentSettingsBorders).val();
			if(isDecimal(borderValue)){
				json.border_size = borderValue;
				renderIframeSettings(false);
			}
		});

		// Toggle the advanced view
		$(remotecontentSettingsAdvancedToggleSettings).click(function(){
			$("#remotecontent_settings_advanced", rootel).toggle();
			isAdvancedSettingsVisible = !isAdvancedSettingsVisible;
			changeAdvancedSettingsArrow();
		});

		// When you click on one of the width units (px or percentage)
		$(remotecontentSettingsWidthUnitClass).click(function()  {
			 var widthUnitValue = $(this).attr("id").split("_")[$(this).attr("id").split("_").length - 1];
			 if(widthUnitValue === "px"){
			 	json.width_unit = widthUnitValue;
			 }else{
			 	json.width_unit = "%";
			 }
			 $(remotecontentSettingsWidthUnitClass).removeClass(remotecontentSettingsWidthUnitSelectedClass);
			 $(this).addClass(remotecontentSettingsWidthUnitSelectedClass);
			 renderIframeSettings(false);
		});

		// When you push the save button..
		$(remotecontentSettingsInsert).click(function()  {
			saveRemoteContent();
		});

		// Cancel it
		$(remotecontentSettingsCancel).click(function()  {
			sdata.container.informCancel(tuid);
		});

		addColorBinding();
	};

	///////////////////////
	// Initial functions //
	///////////////////////

	/**
	 * Function that fills in the input fields in the settings tab.
	 * @param {Object} parameters A JSON object that contains the necessary information.
	 * @param {Boolean} exists Does there exist a previous remotecontent
	 */
	var displaySettings = function(parameters, exists) {
		if(exists && parameters.url){
			json = parameters;
		}else{
			json = {
				border_size: 0,
				border_color: "cccccc",
				height: defaultHeight,
				url: "",
				width: defaultWidth,
				width_unit: defaultWidthUnit
			};
		}
		renderRemoteContentSettings();
		renderIframeSettings(true);
		renderColorContainer();
		addBinding(); // Add binding to the various elements
		changeAdvancedSettingsArrow();
		$(remotecontentSettings).show(); // Show the remotecontent settings
	};

	/*
	 * Is the widget in settings mode or not
	 */
	if (showSettings) {
		$(remotecontentMainContainer).hide();
		$(remotecontentSettings).show();
	}
	else {
		$(remotecontentSettings).hide();
		$(remotecontentMainContainer).show();
	}

	/**
	 * Will fetch the URL and other parameters from the JCR and according to which
	 * view we are in, fill in the settings or display an iframe.
	 */
	var getRemoteContent = function() {
		$.ajax({
			url : Config.URL.SDATA_FETCH_URL.replace(/__PLACEMENT__/, placement).replace(/__TUID__/, tuid).replace(/__NAME__/, "remotecontent"),
			cache: false,
   			success : function(data) {
				// Get a JSON string that contains the necessary information.
				var parameters = $.evalJSON(data);

				if (showSettings) {
					displaySettings(parameters, true); // Fill in the settings page.
				}
				else {
					displayRemoteContent(parameters); // Show the frame
				}
			},
			error: function(status){
				// When the request isn't successful, it means that  there was no existing remotecontent
				// so we show the basic settings.
				displaySettings(null, false);
			}
		});
	};

	getRemoteContent();
};

sdata.widgets.WidgetLoader.informOnLoad("remotecontent");<commit_msg>SAK-16581<commit_after>/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

/*global $, sdata, get_cookie, Config */

var sakai = sakai || {};

sakai.remotecontent = function(tuid, placement, showSettings){

	/////////////////////////////
	// Configuration variables //
	/////////////////////////////

	var rootel = $("#" + tuid);
	var json = false;
	var isAdvancedSettingsVisible = false;

	// Default values
	var defaultWidth = 100;
	var defaultWidthUnit = "%";
	var defaultHeight = 200;

	// Links and labels
	var remotecontent = "#remotecontent";
	var remotecontentSettings = remotecontent + "_settings";
	var remotecontentSettingsAdvanced = remotecontentSettings + "_advanced";
	var remotecontentSettingsAdvancedDown = remotecontentSettingsAdvanced + "_down";
	var remotecontentSettingsAdvancedToggleSettings = remotecontentSettingsAdvanced + "_toggle_settings";
	var remotecontentSettingsAdvancedUp = remotecontentSettingsAdvanced + "_up";
	var remotecontentSettingsBorders = remotecontentSettings + "_borders";
	var remotecontentSettingsCancel = remotecontentSettings + "_cancel";
	var remotecontentSettingsColorContainer = remotecontentSettings + "_color_container";
	var remotecontentSettingsHeight = remotecontentSettings + "_height";
	var remotecontentSettingsInsert = remotecontentSettings + "_insert";
	var remotecontentSettingsPreview = remotecontentSettings + "_preview";
	var remotecontentSettingsPreviewFrame = remotecontentSettingsPreview + "_frame";
	var remotecontentSettingsUrl = remotecontentSettings + "_url";
	var remotecontentSettingsWidth = remotecontentSettings + "_width";

	// Containers
	var remotecontentMainContainer = remotecontent + "_main_container";

	// Classes
	var remotecontentSettingsWidthUnitClass = ".remotecontent_settings_width_unit";
	var remotecontentSettingsWidthUnitSelectedClass = "remotecontent_settings_width_unit_selected";

	// Templates
	var remotecontentSettingsColorContainerTemplate = "remotecontent_settings_color_container_template";
	var remotecontentSettingsTemplate = "remotecontent_settings_template";
	var remotecontentSettingsPreviewTemplate = "remotecontent_settings_preview_template";

	///////////////////////
	// Utility functions //
	///////////////////////

	/**
	 * Check if the value is a decimal or not
	 * @param {Object} value Value that needs to be checked
	 * @return {Boolean}
	 * 	true: is a decimal
	 * 	false: is not a decimal
	 */
	var isDecimal = function(value){
		return (/^\d+$/).test(value);
	};

	/**
	 * Check if the input url is in fact an url or not
	 * @param {String} url Url that needs to be tested
	 * @return {Boolean}
	 * 	true: is an url
	 * 	false: is not an url
	 */
	var isUrl = function(url){
		return (/^http\:\/\/|^https\:\/\//i).test(url);
	};

	/**
	 * Called when the data has been saved to the JCR.
	 */
	var savedDataToJCR = function() {
		sdata.container.informFinish(tuid);
	};

	//////////////////////
	// Render functions //
	//////////////////////

	/**
	 * Render the iframe for the widget in settings mode
	 * @param {Boolean} complete Render the preview completely or only adjust values
	 */
	var renderIframeSettings = function(complete){
		if(complete){
			// We create this object to render the iframe with the default height, width and widthunit
			var jsonDefaultSize = {};
			jsonDefaultSize = json;
			jsonDefaultSize.width = defaultWidth;
			jsonDefaultSize.width_unit = defaultWidthUnit;
			jsonDefaultSize.height = defaultHeight;
			$(remotecontentSettingsPreview).html($.Template.render(remotecontentSettingsPreviewTemplate, json));
		}else{
			$(remotecontentSettingsPreviewFrame).attr("style", "border: " + json.border_size + "px #" + json.border_color + " solid");
		}
	};

	/**
	 * Render the iframe for the widget
	 */
	var renderIframe = function(){
		if(json){
			$(remotecontentMainContainer, rootel).html($.Template.render(remotecontentSettingsPreviewTemplate, json));
		}
	};

	/**
	 * Render the html of the remotecontentsettings
	 */
	var renderRemoteContentSettings = function() {
		if(json){
			$(remotecontentSettings).html($.Template.render(remotecontentSettingsTemplate, json));
		}
	};

	/**
	 * Render the color container
	 */
	var renderColorContainer = function(){
		if(json){
			$(remotecontentSettingsColorContainer).html($.Template.render(remotecontentSettingsColorContainerTemplate, json));
		}
	};

	//////////////////////
	// Global functions //
	//////////////////////

	/**
	 * Display the iframe in normal mode
	 * @param {Object} parameters JSON object that contains the necessary information for the iframe
	 */
	var displayRemoteContent = function(parameters) {
		json = parameters;
		renderIframe();
	};

	/**
	 * Save the remotecontent to the jcr
	 */
	var saveRemoteContent = function() {
		if (json.url !== "") {
			var str = $.toJSON(json); // Convert the posts to a JSON string
			var saveUrl = Config.URL.SDATA_FETCH_BASIC_URL.replace(/__PLACEMENT__/, placement).replace(/__TUID__/, tuid);
			sdata.widgets.WidgetPreference.save(saveUrl, "remotecontent", str, savedDataToJCR);
		} else {
			alert("Please specify a URL");
		}
	};

	/**
	 * Change the direction (up/down) of the arrow for the advanced settings
	 */
	var changeAdvancedSettingsArrow = function(){
		if (isAdvancedSettingsVisible) {
			$(remotecontentSettingsAdvancedDown, rootel).hide();
			$(remotecontentSettingsAdvancedUp, rootel).show();
		}
		else {
			$(remotecontentSettingsAdvancedUp, rootel).hide();
			$(remotecontentSettingsAdvancedDown, rootel).show();
		}
	};

	//////////////
	// Bindings //
	//////////////

	/*
	 * Add binding to the color boxes
	 */
	var addColorBinding = function(){
		$(".remotecontent_settings_color").click(function() {
			json.border_color = $(this).attr("id").split("_")[$(this).attr("id").split("_").length - 1];
			renderIframeSettings(false);
			renderColorContainer();
			addColorBinding();
		});
	};

	/*
	 * Add binding to all the elements
	 */
	var addBinding = function(){

		// Change the url for the iFrame
		$(remotecontentSettingsUrl).change(function() {
			var urlValue = $(this).val();
			if (urlValue !== "") {
				// Check if someone already wrote http inside the url
				if (!isUrl(urlValue)) {
					urlValue = 'http://' + urlValue;
				}
				json.url = urlValue;
				renderIframeSettings(true);
			}
		});

		// Change the iframe width
		$(remotecontentSettingsWidth).change(function() {
			var widthValue = $(remotecontentSettingsWidth).val();

			if(isDecimal(widthValue)){
				json.width = widthValue;
			}
			renderIframeSettings(false);
		});

		// Change the iframe height
		$(remotecontentSettingsHeight).change(function() {
			var heightValue = $(remotecontentSettingsHeight).val();

			if(isDecimal(heightValue)){
				json.height = heightValue;
			}
			renderIframeSettings(false);
		});

		// Change the border width
		$(remotecontentSettingsBorders).change(function() {
			var borderValue = $(remotecontentSettingsBorders).val();
			if(isDecimal(borderValue)){
				json.border_size = borderValue;
				renderIframeSettings(false);
			}
		});

		// Toggle the advanced view
		$(remotecontentSettingsAdvancedToggleSettings).click(function(){
			$("#remotecontent_settings_advanced", rootel).toggle();
			isAdvancedSettingsVisible = !isAdvancedSettingsVisible;
			changeAdvancedSettingsArrow();
		});

		// When you click on one of the width units (px or percentage)
		$(remotecontentSettingsWidthUnitClass).click(function()  {
			 var widthUnitValue = $(this).attr("id").split("_")[$(this).attr("id").split("_").length - 1];
			 if(widthUnitValue === "px"){
			 	json.width_unit = widthUnitValue;
			 }else{
			 	json.width_unit = "%";
			 }
			 $(remotecontentSettingsWidthUnitClass).removeClass(remotecontentSettingsWidthUnitSelectedClass);
			 $(this).addClass(remotecontentSettingsWidthUnitSelectedClass);
			 renderIframeSettings(false);
		});

		// When you push the save button..
		$(remotecontentSettingsInsert).click(function()  {
			saveRemoteContent();
		});

		// Cancel it
		$(remotecontentSettingsCancel).click(function()  {
			sdata.container.informCancel(tuid);
		});

		addColorBinding();
	};

	///////////////////////
	// Initial functions //
	///////////////////////

	/**
	 * Function that fills in the input fields in the settings tab.
	 * @param {Object} parameters A JSON object that contains the necessary information.
	 * @param {Boolean} exists Does there exist a previous remotecontent
	 */
	var displaySettings = function(parameters, exists) {
		if(exists && parameters.url){
			json = parameters;
		}else{
			json = {
				border_size: 0,
				border_color: "cccccc",
				height: defaultHeight,
				url: "",
				width: defaultWidth,
				width_unit: defaultWidthUnit
			};
		}
		renderRemoteContentSettings();
		renderIframeSettings(true);
		renderColorContainer();
		addBinding(); // Add binding to the various elements
		changeAdvancedSettingsArrow();
		$(remotecontentSettings).show(); // Show the remotecontent settings
	};

	/*
	 * Is the widget in settings mode or not
	 */
	if (showSettings) {
		$(remotecontentMainContainer).hide();
		$(remotecontentSettings).show();
	}
	else {
		$(remotecontentSettings).hide();
		$(remotecontentMainContainer).show();
	}

	/**
	 * Will fetch the URL and other parameters from the JCR and according to which
	 * view we are in, fill in the settings or display an iframe.
	 */
	var getRemoteContent = function() {
		$.ajax({
			url : Config.URL.SDATA_FETCH_URL.replace(/__PLACEMENT__/, placement).replace(/__TUID__/, tuid).replace(/__NAME__/, "remotecontent"),
			cache: false,
   			success : function(data) {
				// Get a JSON string that contains the necessary information.
				var parameters = $.evalJSON(data);

				if (showSettings) {
					displaySettings(parameters, true); // Fill in the settings page.
				}
				else {
					displayRemoteContent(parameters); // Show the frame
				}
			},
			error: function(status){
				// When the request isn't successful, it means that  there was no existing remotecontent
				// so we show the basic settings.
				displaySettings(null, false);
			}
		});
	};

	getRemoteContent();
};

sdata.widgets.WidgetLoader.informOnLoad("remotecontent");<|endoftext|>


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

  angular.module('cla.controllers.provider').
    controller('AcceptRejectCaseCtrl', ['$scope', '$modal', 'flash', 'postal', '$state', function($scope, $modal, flash, postal, $state){
      $scope.accept = function() {
        this.case.$accept_case().then(function(data) {
          flash('Case accepted successfully');
          // refreshing the logs
          postal.publish({
            channel : 'models',
            topic   : 'Log.refresh'
          });
          $scope.case = data;
        });
      };

      $scope.reject = function() {
        var modalOpts = {
          templateUrl: 'case_detail.outcome_modal.with_feedback.html',
          controller: 'OutcomesFeedbackModalCtl',
          resolve: {
            tplVars: function() {
              return {
                title: 'Reject case'
              };
            },
            case: function() { return $scope.case; },
            event_key: function() { return 'reject_case'; },  //this is also the function name on Case model
            notes: function() { return ''; }
          }
        };
        var onSuccess = function (result) {
          if (result) {
            flash('success', 'Case ' + $scope.case.reference + ' rejected successfully');
          } else {
            flash('error', 'There was a problem rejecting this case');
          }
          $state.go('case_list');
        };

        $modal.open(modalOpts).result.then(onSuccess);
      };

      $scope.reopen = function() {
        var modalOpts = {
          templateUrl: 'case_detail.outcome_modal.html',
          controller: 'ImplicitOutcomeModalCtl',
          resolve: {
            tplVars: function() {
              return {
                title: 'Reopen case'
              };
            },
            case: function() { return $scope.case; },
            model_action: function() { return 'reopen_case'; },
            notes: function() { return ''; }
          }
        };
        var onSuccess = function (result) {
          if (result) {
            flash('Case re-opened successfully');
            postal.publish({
              channel : 'models',
              topic   : 'Log.refresh'
            });
            $scope.case = result;
          } else {
            flash('error', 'There was a problem closing this case');
          }
        };

        $modal.open(modalOpts).result.then(onSuccess);
      };

      $scope.split = function() {
        $modal.open({
          templateUrl: 'provider/case_detail.split.html',
          controller: 'SplitCaseCtrl',
          resolve: {
            'case': function() { return $scope.case; },
            'diagnosis': function() { return $scope.diagnosis; },
            provider_category: ['Category', function(Category) {
              return Category.get({code: $scope.diagnosis.category}).$promise;
            }],
            categories: ['Category', function(Category) {
              return Category.query().$promise;
            }]
          }
        });
      };
    }]
  );
})();
<commit_msg>Updated copy<commit_after>(function(){
  'use strict';

  angular.module('cla.controllers.provider').
    controller('AcceptRejectCaseCtrl', ['$scope', '$modal', 'flash', 'postal', '$state', function($scope, $modal, flash, postal, $state){
      $scope.accept = function() {
        this.case.$accept_case().then(function(data) {
          flash('Case accepted successfully');
          // refreshing the logs
          postal.publish({
            channel : 'models',
            topic   : 'Log.refresh'
          });
          $scope.case = data;
        });
      };

      $scope.reject = function() {
        var modalOpts = {
          templateUrl: 'case_detail.outcome_modal.with_feedback.html',
          controller: 'OutcomesFeedbackModalCtl',
          resolve: {
            tplVars: function() {
              return {
                title: 'Reject case'
              };
            },
            case: function() { return $scope.case; },
            event_key: function() { return 'reject_case'; },  //this is also the function name on Case model
            notes: function() { return ''; }
          }
        };
        var onSuccess = function (result) {
          if (result) {
            flash('success', 'Case ' + $scope.case.reference + ' rejected successfully');
          } else {
            flash('error', 'There was a problem rejecting this case');
          }
          $state.go('case_list');
        };

        $modal.open(modalOpts).result.then(onSuccess);
      };

      $scope.reopen = function() {
        var modalOpts = {
          templateUrl: 'case_detail.outcome_modal.html',
          controller: 'ImplicitOutcomeModalCtl',
          resolve: {
            tplVars: function() {
              return {
                title: 'Reopen case'
              };
            },
            case: function() { return $scope.case; },
            model_action: function() { return 'reopen_case'; },
            notes: function() { return ''; }
          }
        };
        var onSuccess = function (result) {
          if (result) {
            flash('Case re-opened successfully');
            postal.publish({
              channel : 'models',
              topic   : 'Log.refresh'
            });
            $scope.case = result;
          } else {
            flash('error', 'There was a problem re-opening this case');
          }
        };

        $modal.open(modalOpts).result.then(onSuccess);
      };

      $scope.split = function() {
        $modal.open({
          templateUrl: 'provider/case_detail.split.html',
          controller: 'SplitCaseCtrl',
          resolve: {
            'case': function() { return $scope.case; },
            'diagnosis': function() { return $scope.diagnosis; },
            provider_category: ['Category', function(Category) {
              return Category.get({code: $scope.diagnosis.category}).$promise;
            }],
            categories: ['Category', function(Category) {
              return Category.query().$promise;
            }]
          }
        });
      };
    }]
  );
})();
<|endoftext|>


<commit_before>/*
 * Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
 *
 * Copyright 2013 - 2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

_dynamicInclude($includeFolder);
_dynamicInclude("../../common/showroom_common.js");
_dynamicInclude("../../common/login_credentials.js")

var testCase = new TestCase(60, 70);

var $username = "sakuli";
var $comment = "Auch Sakuli ist begeistert und will jetzt Kaffee!";

try {
    var browser = new Application(BROWSER_NAME);
    browser.focus();
    maximize(browser, "browser_maximize");

    load();
	testCase.endOfStep("Load");

    login(3);
	testCase.endOfStep("Login");

    search("Sa", "528", 3);
	testCase.endOfStep("Search Ticket 528");

	addComment($comment);
	testCase.endOfStep("Add Comment");

    checkComment($comment);
	testCase.endOfStep("Check Comment");

    env.sleep(3);

} catch (e) {
    testCase.handleException(e);
}
finally {
    end();
}

function addComment($comment){

	openComment();
    writeComment($comment);
	submitComment();
}

function openComment() {

    _highlight(_link("Kommentar"));
    _click(_link("Kommentar"));
}

function submitComment() {

    _highlight(_button("Hinzufügen"));
    _click(_button("Hinzufügen"));
}

function checkComment($comment) {
    _highlight(_div("submain historygroup"));
    env.sleep(1);
    $testString = _getText(_div("/richContent_.*/", _div("submain historygroup")));
    _highlight(_div("/richContent_.*/", _div("submain historygroup")));

    var isIdentical = false;
    if ($testString == $comment) {
        isIdentical = true;

    } else {
        throw "The comment function did not perform correctly!";
    }
}
<commit_msg>change comment ticket id<commit_after>/*
 * Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
 *
 * Copyright 2013 - 2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

_dynamicInclude($includeFolder);
_dynamicInclude("../../common/showroom_common.js");
_dynamicInclude("../../common/login_credentials.js")

var testCase = new TestCase(60, 70);

var $username = "sakuli";
var $comment = "Auch Sakuli ist begeistert und will jetzt Kaffee!";

try {
    var browser = new Application(BROWSER_NAME);
    browser.focus();
    maximize(browser, "browser_maximize");

    load();
	testCase.endOfStep("Load");

    login(3);
	testCase.endOfStep("Login");

    search("Sa", "1146", 3);
    testCase.endOfStep("Search Ticket 1146");

	addComment($comment);
	testCase.endOfStep("Add Comment");

    checkComment($comment);
	testCase.endOfStep("Check Comment");

    env.sleep(3);

} catch (e) {
    testCase.handleException(e);
}
finally {
    end();
}

function addComment($comment){

	openComment();
    writeComment($comment);
	submitComment();
}

function openComment() {

    _highlight(_link("Kommentar"));
    _click(_link("Kommentar"));
}

function submitComment() {

    _highlight(_button("Hinzufügen"));
    _click(_button("Hinzufügen"));
}

function checkComment($comment) {
    _highlight(_div("submain historygroup"));
    env.sleep(1);
    $testString = _getText(_div("/richContent_.*/", _div("submain historygroup")));
    _highlight(_div("/richContent_.*/", _div("submain historygroup")));

    var isIdentical = false;
    if ($testString == $comment) {
        isIdentical = true;

    } else {
        throw "The comment function did not perform correctly!";
    }
}
<|endoftext|>


<commit_before>/**
 * Controller que se usa para crear el grafico de cuantos arqueros hay en cada
 * estado en cada mes.
 */
var CantidadEstadosPorMesController = Class.$extend({

    /**
     * Constructor.
     *
     * @param {jQuery} element: el elemento del DOM en donde es que se tiene que
     *                          graficar toda la informacion.
     *
     * @param {APIManager} apiManager: el manager que se va a usar para obtener
     *                                 toda la informacion a graficar.
     *
     * @param {SearchController} searchController: el controller que se quiere usar
     *                                             para filtrar toda la informacion
     *
     * @param {DatabaseInformation} databaseInformation: tiene toda la informacion sobre
     *                                                   los schemas de la base de datos
     */
    __init__: function(element, apiManager, searchController, databaseInformation) {
        this.$element = element;
        this.apiManager = apiManager;
        this.searchController = searchController;
        this.databaseInformation = databaseInformation;

        this.missingCallbacks = null;
        this.responseData = null;
    },

    /**
     * Se encarga de hacer el llamado ajax para obtener la informacion a graficar
     */
    render: function() {
        this.$element.mask('Loading');

        var filtersData = utils.translateFiltersToAPI(this.searchController.filters);

        var ajaxData = {
                orderBy: 'desde',
                orderDirection: 'ASC',
                columns: ['desde', 'hasta', 'id_estado_arquero'],
                filters: filtersData
        };

        this.missingCallbacks = [true, true];
        this.responseData = [null, null];
        this.apiManager.ajaxCall('historia_estado_arquero/',
                                 ajaxData,
                                 'GET',
                                 $.proxy(this.renderInformation, this));

    },

    /**
     * Se encarga de renderar toda la informacion del grafico teniendo en cuenta
     * la respuesta del servidor.
     *
     *
     * En este caso, la inforamcion de los dos requests viene dado en el siguiente
     * formato:
     *
     * {
     *      values: [
     *          ['01/01/2013', '04/01/2013', 2],
     *          ['01/01/2013', '04/01/2013', 3],
     *          ...
     *      ]
     * }
     *
     * donde:
     *
     *  - el primer valor corresponde al mes del estado
     *  - el segundo corresponde al id del estado
     *  - el tercer valor corresponde al count de la cantidad de clientes.
     */
    renderInformation: function(data) {
        // voy a pasar todas las felcha a moment para poder
        // parsearlas mas rapido
        var parsedData = [];
        for (var i = 0; i < data.values.length; i++) {
            var tmp = data.values[i];
            parsedData.push([
                    moment(tmp[0], consts.DATE_RESPONSE_FORMAT),
                    tmp[1] !== null ? moment(tmp[1], consts.DATE_RESPONSE_FORMAT) : null,
                    tmp[2]
            ]);
        }

        // ahora voy a ir juntando la informacion mes a mes, para ver cual
        // es el maximo y cual es el minimo
        var minDate = _.min(parsedData, function(d) {
            return d[0];
        })[0];
        var maxDate = _.max(parsedData, function(d) {
            return d[0];
        })[0];

        var datesDifference = utils.getMonthsDifference(minDate, maxDate);
        var finalData = [];
        var columnInformation = this.databaseInformation.getColumnInformation('historia_estado_arquero',
                                                                              'id_estado_arquero');
        for (var id = 1; id <= 4; id++) {
            var idData = [];
            for (var monthIndex = 0; monthIndex <= datesDifference; monthIndex++) {
                idData.push(0);
            }
            finalData.push({
                name: columnInformation.getConstValue(id).value,
                data: idData
            });
        }

        var xCategories = [];
        var aux = moment(minDate);
        for (var monthIndex = 0; monthIndex <= datesDifference; monthIndex++) {
            xCategories.push(aux);
            aux = moment(aux);
            aux.add('months', 1);
        }

        // ahora para cada id tengo que contar la cantidad de valores
        // que hay
        for (var id = 1; id <= 4; id++) {
            var dataForId = finalData[id - 1].data;
            for (var j = 0; j < parsedData.length; j++) {
                var currentData = parsedData[j];
                if (currentData[2] !== id) {
                    continue;
                }

                // ahora si estoy en el mismo id, entonces me tengo que
                // fijar que a que meses tengo que sumar o si es solo uno
                if (id === consts.ESTADO_ARQUERO.activo || id === consts.ESTADO_ARQUERO.pasivo) {
                    // en este caso se tiene que acumular mes a mes el valor
                    // en el que estuvo en este estado.
                    var startingIndex = utils.getMonthsDifference(minDate, currentData[0]);
                    if (currentData[1] === null) {
                        for (var k = startingIndex; k < dataForId.length; k++) {
                            dataForId[k] += 1;
                        }

                    } else {
                        // en este caso tengo una fecha fin por lo que se hasta
                        // que mes tengo que acumular
                        endingIndex = utils.getMonthsDifference(currentData[1], minDate);
                        for (var k = startingIndex; k < endingIndex; k++) {
                            dataForId[k] += 1;
                        }
                    }
                } else {
                    // en este caso solo sumo al mes correspondiente
                    var startingIndex = utils.getMonthsDifference(minDate, currentData[0]);
                    dataForId[startingIndex] += 1;
                }
            }
        }

        this.$element.unmask();
        this.$element.find('.chart-container').highcharts({
            chart: {
                type: 'column'
            },
            xAxis: {
                type: 'datetime',
                categories: xCategories
            },
            series: finalData
        });
    }

});
<commit_msg>Solved #21<commit_after>/**
 * Controller que se usa para crear el grafico de cuantos arqueros hay en cada
 * estado en cada mes.
 */
var CantidadEstadosPorMesController = Class.$extend({

    /**
     * Constructor.
     *
     * @param {jQuery} element: el elemento del DOM en donde es que se tiene que
     *                          graficar toda la informacion.
     *
     * @param {APIManager} apiManager: el manager que se va a usar para obtener
     *                                 toda la informacion a graficar.
     *
     * @param {SearchController} searchController: el controller que se quiere usar
     *                                             para filtrar toda la informacion
     *
     * @param {DatabaseInformation} databaseInformation: tiene toda la informacion sobre
     *                                                   los schemas de la base de datos
     */
    __init__: function(element, apiManager, searchController, databaseInformation) {
        this.$element = element;
        this.apiManager = apiManager;
        this.searchController = searchController;
        this.databaseInformation = databaseInformation;

        this.missingCallbacks = null;
        this.responseData = null;
    },

    /**
     * Se encarga de hacer el llamado ajax para obtener la informacion a graficar
     */
    render: function() {
        this.$element.mask('Loading');

        var filtersData = utils.translateFiltersToAPI(this.searchController.filters);

        var ajaxData = {
                orderBy: 'desde',
                orderDirection: 'ASC',
                columns: ['desde', 'hasta', 'id_estado_arquero'],
                filters: filtersData
        };

        this.missingCallbacks = [true, true];
        this.responseData = [null, null];
        this.apiManager.ajaxCall('historia_estado_arquero/',
                                 ajaxData,
                                 'GET',
                                 $.proxy(this.renderInformation, this));

    },

    /**
     * Se encarga de renderar toda la informacion del grafico teniendo en cuenta
     * la respuesta del servidor.
     *
     *
     * En este caso, la inforamcion de los dos requests viene dado en el siguiente
     * formato:
     *
     * {
     *      values: [
     *          ['01/01/2013', '04/01/2013', 2],
     *          ['01/01/2013', '04/01/2013', 3],
     *          ...
     *      ]
     * }
     *
     * donde:
     *
     *  - el primer valor corresponde al mes del estado
     *  - el segundo corresponde al id del estado
     *  - el tercer valor corresponde al count de la cantidad de clientes.
     */
    renderInformation: function(data) {
        // voy a pasar todas las felcha a moment para poder
        // parsearlas mas rapido
        var parsedData = [];
        for (var i = 0; i < data.values.length; i++) {
            var tmp = data.values[i];
            parsedData.push([
                    moment(tmp[0], consts.DATE_RESPONSE_FORMAT),
                    tmp[1] !== null ? moment(tmp[1], consts.DATE_RESPONSE_FORMAT) : null,
                    tmp[2]
            ]);
        }

        if (_.isEmpty(parsedData)) {
            this.$element.unmask();
            this.$element.find('.chart-container').html('
No se encontro información
');
            return
        }

        // ahora voy a ir juntando la informacion mes a mes, para ver cual
        // es el maximo y cual es el minimo
        var minDate = _.min(parsedData, function(d) {
            return d[0];
        })[0];
        var maxDate = _.max(parsedData, function(d) {
            return d[0];
        })[0];

        var datesDifference = utils.getMonthsDifference(minDate, maxDate);
        var finalData = [];
        var columnInformation = this.databaseInformation.getColumnInformation('historia_estado_arquero',
                                                                              'id_estado_arquero');
        for (var id = 1; id <= 4; id++) {
            var idData = [];
            for (var monthIndex = 0; monthIndex <= datesDifference; monthIndex++) {
                idData.push(0);
            }
            finalData.push({
                name: columnInformation.getConstValue(id).value,
                data: idData
            });
        }

        var xCategories = [];
        var aux = moment(minDate);
        for (var monthIndex = 0; monthIndex <= datesDifference; monthIndex++) {
            xCategories.push(aux);
            aux = moment(aux);
            aux.add('months', 1);
        }

        // ahora para cada id tengo que contar la cantidad de valores
        // que hay
        for (var id = 1; id <= 4; id++) {
            var dataForId = finalData[id - 1].data;
            for (var j = 0; j < parsedData.length; j++) {
                var currentData = parsedData[j];
                if (currentData[2] !== id) {
                    continue;
                }

                // ahora si estoy en el mismo id, entonces me tengo que
                // fijar que a que meses tengo que sumar o si es solo uno
                if (id === consts.ESTADO_ARQUERO.activo || id === consts.ESTADO_ARQUERO.pasivo) {
                    // en este caso se tiene que acumular mes a mes el valor
                    // en el que estuvo en este estado.
                    var startingIndex = utils.getMonthsDifference(minDate, currentData[0]);
                    if (currentData[1] === null) {
                        for (var k = startingIndex; k < dataForId.length; k++) {
                            dataForId[k] += 1;
                        }

                    } else {
                        // en este caso tengo una fecha fin por lo que se hasta
                        // que mes tengo que acumular
                        endingIndex = utils.getMonthsDifference(currentData[1], minDate);
                        for (var k = startingIndex; k < endingIndex; k++) {
                            dataForId[k] += 1;
                        }
                    }
                } else {
                    // en este caso solo sumo al mes correspondiente
                    var startingIndex = utils.getMonthsDifference(minDate, currentData[0]);
                    dataForId[startingIndex] += 1;
                }
            }
        }

        this.$element.unmask();
        this.$element.find('.chart-container').highcharts({
            chart: {
                type: 'column'
            },
            xAxis: {
                type: 'datetime',
                categories: xCategories
            },
            series: finalData
        });
    }

});
<|endoftext|>


<commit_before>import Ember from 'ember';
import base from './changes';
import { array } from '../../util/computed';
import assert from '../../util/assert';

const {
  computed: { gt },
  run: { next, cancel }
} = Ember;

export default (...deps) => base(...deps).extend({

  _suspended: 0,
  _queue: array(),

  suspended: gt('_suspended', 0).readOnly(),

  _trigger(...args) {
    this.get('_queue').pushObject(args);
    this._enqueueFlush();
  },

  _enqueueFlush() {
    cancel(this.__flush);
    this.__flush = next(this, this._flush);
  },

  _flush() {
    if(this.get('_suspended') > 0) {
      return;
    }
    var queue = this.get('_queue');
    var change = queue.shiftObject();
    if(change) {
      this.trigger(...change);
      this._enqueueFlush();
    }
  },

  _resume() {
    this.decrementProperty('_suspended');
    if(this.get('_suspended') === 0) {
      this._enqueueFlush();
    }
  },

  suspend() {
    this.incrementProperty('_suspended');
    let fn = () => {
      assert({ error: 'internal', reason: 'resume already called' }, !fn._called);
      this._resume();
      fn._called = true;
    };
    fn._called = false;
    return fn;
  },

  willDestroy() {
    cancel(this.__flush);
    this._super();
  }

});
<commit_msg>trigger change immediately if not suspended and queue is empty<commit_after>import Ember from 'ember';
import base from './changes';
import { array } from '../../util/computed';
import assert from '../../util/assert';

const {
  computed: { gt },
  run: { next, cancel }
} = Ember;

export default (...deps) => base(...deps).extend({

  _suspended: 0,
  _queue: array(),

  suspended: gt('_suspended', 0).readOnly(),

  _trigger(...args) {
    if(this.get('_suspended') === 0 && this.get('_queue.length') === 0) {
      this.trigger(...args);
    } else {
      this.get('_queue').pushObject(args);
      this._enqueueFlush();
    }
  },

  _enqueueFlush() {
    cancel(this.__flush);
    this.__flush = next(this, this._flush);
  },

  _flush() {
    if(this.get('_suspended') > 0) {
      return;
    }
    var queue = this.get('_queue');
    var change = queue.shiftObject();
    if(change) {
      this.trigger(...change);
      this._enqueueFlush();
    }
  },

  _resume() {
    this.decrementProperty('_suspended');
    if(this.get('_suspended') === 0) {
      this._enqueueFlush();
    }
  },

  suspend() {
    this.incrementProperty('_suspended');
    let fn = () => {
      assert({ error: 'internal', reason: 'resume already called' }, !fn._called);
      this._resume();
      fn._called = true;
    };
    fn._called = false;
    return fn;
  },

  willDestroy() {
    cancel(this.__flush);
    this._super();
  }

});
<|endoftext|>


<commit_before>/**
 * @namespace handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out
 */
openerp.base.list.editable = function (openerp) {
    var KEY_RETURN = 13,
        KEY_ESCAPE = 27;

    // editability status of list rows
    openerp.base.ListView.prototype.defaults.editable = null;

    var old_init = openerp.base.ListView.prototype.init,
        old_actual_search = openerp.base.ListView.prototype.do_actual_search,
        old_add_record = openerp.base.ListView.prototype.do_add_record,
        old_on_loaded = openerp.base.ListView.prototype.on_loaded;
    // TODO: not sure second @lends on existing item is correct, to check
    _.extend(openerp.base.ListView.prototype, /** @lends openerp.base.ListView# */{
        init: function () {
            var self = this;
            old_init.apply(this, arguments);
            $(this.groups).bind({
                'edit': function (e, id, dataset) {
                    self.do_edit(dataset.index, id, dataset);
                },
                'saved': function () {
                    if (self.groups.get_selection().length) {
                        return;
                    }
                    self.compute_aggregates();
                }
            })
        },
        /**
         * Handles the activation of a record in editable mode (making a record
         * editable), called *after* the record has become editable.
         *
         * The default behavior is to setup the listview's dataset to match
         * whatever dataset was provided by the editing List
         *
         * @param {Number} index index of the record in the dataset
         * @param {Object} id identifier of the record being edited
         * @param {openerp.base.DataSet} dataset dataset in which the record is available
         */
        do_edit: function (index, id, dataset) {
            _.extend(this.dataset, dataset);
        },
        /**
         * Sets editability status for the list, based on defaults, view
         * architecture and the provided flag, if any.
         *
         * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom".
         */
        set_editable: function (force) {
            // If ``force``, set editability to bottom
            // otherwise rely on view default
            // view' @editable is handled separately as we have not yet
            // fetched and processed the view at this point.
            this.options.editable = (
                    (force && "bottom")
                    || this.defaults.editable);
        },
        /**
         * Replace do_actual_search to handle editability process
         */
        do_actual_search: function (results) {
            this.set_editable(results.context['set_editable']);
            old_actual_search.call(this, results);
        },
        /**
         * Replace do_add_record to handle editability (and adding new record
         * as an editable row at the top or bottom of the list)
         */
        do_add_record: function () {
            if (this.options.editable) {
                this.groups.new_record();
            } else {
                old_add_record.call(this);
            }
        },
        on_loaded: function (data, grouped) {
            // tree/@editable takes priority on everything else if present.
            this.options.editable = data.fields_view.arch.attrs.editable || this.options.editable;
            return old_on_loaded.call(this, data, grouped);
        }
    });

    _.extend(openerp.base.ListView.Groups.prototype, /** @lends openerp.base.ListView.Groups# */{
        passtrough_events: openerp.base.ListView.Groups.prototype.passtrough_events + " edit saved",
        new_record: function () {
            // TODO: handle multiple children
            this.children[null].new_record();
        }
    });

    var old_list_row_clicked = openerp.base.ListView.List.prototype.row_clicked;
    _.extend(openerp.base.ListView.List.prototype, /** @lends openerp.base.ListView.List */{
        row_clicked: function (event) {
            if (!this.options.editable) {
                return old_list_row_clicked.call(this, event);
            }
            this.edit_record();
        },
        /**
         * Checks if a record is being edited, and if so cancels it
         */
        cancel_pending_edition: function () {
            if (!this.edition) {
                return;
            }

            if (this.edition_index !== null) {
                this.reload_record(this.edition_index);
            }
            this.edition_form.stop();
            this.edition_form.$element.remove();
            delete this.edition_form;
            delete this.edition_index;
            delete this.edition;
        },
        render_row_as_form: function (row) {
            this.cancel_pending_edition();

            var self = this;
            var $new_row = $('<tr>', {
                    id: _.uniqueId('oe-editable-row-'),
                    'class': $(row).attr('class'),
                    click: function (e) {e.stopPropagation();}
                })
                .keyup(function (e) {
                    switch (e.which) {
                        case KEY_RETURN:
                            self.save_row(true);
                            break;
                        case KEY_ESCAPE:
                            self.cancel_edition();
                            break;
                        default:
                            return;
                    }
                })
                .delegate('button.oe-edit-row-save', 'click', function () {
                    self.save_row();
                })
                .delegate('button.oe-edit-row-cancel', 'click', function () {
                    self.cancel_edition();
                });
            if (row) {
                $new_row.replaceAll(row);
            } else if (this.options.editable === 'top') {
                this.$current.prepend($new_row);
            } else if (this.options.editable) {
                this.$current.append($new_row);
            }
            this.edition = true;
            this.edition_index = this.dataset.index;
            this.edition_form = _.extend(new openerp.base.FormView(
                    null, this.group.view.session, $new_row.attr('id'),
                    this.dataset, false), {
                template: 'ListView.row.form',
                registry: openerp.base.list.form.widgets
            });
            $.when(this.edition_form.on_loaded({fields_view: this.get_fields_view()})).then(function () {
                // put in $.when just in case  FormView.on_loaded becomes asynchronous
                $new_row.find('td')
                      .addClass('oe-field-cell')
                      .removeAttr('width')
                  .end()
                  .find('td:first').removeClass('oe-field-cell').end()
                  .find('td:last').removeClass('oe-field-cell').end();
                // pad in case of groupby
                _(self.columns).each(function (column) {
                    if (column.meta) {
                        $new_row.prepend('<td>');
                    }
                });

                self.edition_form.do_show();
            });
        },
        /**
         * Saves the current row, and triggers the edition of its following
         * sibling if asked.
         *
         * @param {Boolean} [edit_next=false] should the next row become editable
         */
        save_row: function (edit_next) {
            var self = this;
            this.edition_form.do_save(function (result) {
                self.reload_record(self.dataset.index, true).then(function () {
                    self.edition_form.stop();
                    delete self.edition_form;
                    delete self.edition_index;
                    delete self.edition;

                    $(self).trigger('saved', [self.dataset]);
                    if (!edit_next) {
                        return;
                    }
                    if (result.created) {
                        self.new_record();
                        return;
                    }
                    self.dataset.next();
                    self.edit_record();
                });
            }, this.options.editable === 'top');
        },
        /**
         * Cancels the edition of the row for the current dataset index
         */
        cancel_edition: function () {
            this.cancel_pending_edition();
        },
        /**
         * Edits record currently selected via dataset
         */
        edit_record: function () {
            this.render_row_as_form(
                this.$current.children(
                    _.sprintf('[data-index=%d]',
                            this.dataset.index)));
            $(this).trigger(
                'edit',
                [this.rows[this.dataset.index].data.id.value, this.dataset]);
        },
        new_record: function () {
            this.dataset.index = null;
            this.render_row_as_form();
        }
    });
    openerp.base.list = {form: {}};
    openerp.base.list.form.WidgetFrame = openerp.base.form.WidgetFrame.extend({
        template: 'ListView.row.frame'
    });
    var form_widgets = openerp.base.form.widgets;
    openerp.base.list.form.widgets = form_widgets.clone({
        'frame': 'openerp.base.list.form.WidgetFrame'
    });
    // All form widgets inherit a problematic behavior from
    // openerp.base.form.WidgetFrame: the cell itself is removed when invisible
    // whether it's @invisible or @attrs[invisible]. In list view, only the
    // former should completely remove the cell. We need to override update_dom
    // on all widgets since we can't just hit on widget itself (I think)
    var list_form_widgets = openerp.base.list.form.widgets;
    _(list_form_widgets.map).each(function (widget_path, key) {
        if (key === 'frame') { return; }
        var new_path = 'openerp.base.list.form.' + key;

        openerp.base.list.form[key] = (form_widgets.get_object(key)).extend({
            update_dom: function () {
                this.$element.children().css('visibility', '');
                if (this.invisible && this.node.attrs.invisible !== '1') {
                    this.$element.children().css('visibility', 'hidden');
                } else {
                    this._super();
                }
            }
        });
        list_form_widgets.add(key, new_path);
    });
};
<commit_msg>[FIX] When using return on an action button, triggers both click and keyup, so we have to disable keyup as well on buttons in editable list, or it gets forwarded to the list itself (and executes action + double saves)<commit_after>/**
 * @namespace handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out
 */
openerp.base.list.editable = function (openerp) {
    var KEY_RETURN = 13,
        KEY_ESCAPE = 27;

    // editability status of list rows
    openerp.base.ListView.prototype.defaults.editable = null;

    var old_init = openerp.base.ListView.prototype.init,
        old_actual_search = openerp.base.ListView.prototype.do_actual_search,
        old_add_record = openerp.base.ListView.prototype.do_add_record,
        old_on_loaded = openerp.base.ListView.prototype.on_loaded;
    // TODO: not sure second @lends on existing item is correct, to check
    _.extend(openerp.base.ListView.prototype, /** @lends openerp.base.ListView# */{
        init: function () {
            var self = this;
            old_init.apply(this, arguments);
            $(this.groups).bind({
                'edit': function (e, id, dataset) {
                    self.do_edit(dataset.index, id, dataset);
                },
                'saved': function () {
                    if (self.groups.get_selection().length) {
                        return;
                    }
                    self.compute_aggregates();
                }
            })
        },
        /**
         * Handles the activation of a record in editable mode (making a record
         * editable), called *after* the record has become editable.
         *
         * The default behavior is to setup the listview's dataset to match
         * whatever dataset was provided by the editing List
         *
         * @param {Number} index index of the record in the dataset
         * @param {Object} id identifier of the record being edited
         * @param {openerp.base.DataSet} dataset dataset in which the record is available
         */
        do_edit: function (index, id, dataset) {
            _.extend(this.dataset, dataset);
        },
        /**
         * Sets editability status for the list, based on defaults, view
         * architecture and the provided flag, if any.
         *
         * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom".
         */
        set_editable: function (force) {
            // If ``force``, set editability to bottom
            // otherwise rely on view default
            // view' @editable is handled separately as we have not yet
            // fetched and processed the view at this point.
            this.options.editable = (
                    (force && "bottom")
                    || this.defaults.editable);
        },
        /**
         * Replace do_actual_search to handle editability process
         */
        do_actual_search: function (results) {
            this.set_editable(results.context['set_editable']);
            old_actual_search.call(this, results);
        },
        /**
         * Replace do_add_record to handle editability (and adding new record
         * as an editable row at the top or bottom of the list)
         */
        do_add_record: function () {
            if (this.options.editable) {
                this.groups.new_record();
            } else {
                old_add_record.call(this);
            }
        },
        on_loaded: function (data, grouped) {
            // tree/@editable takes priority on everything else if present.
            this.options.editable = data.fields_view.arch.attrs.editable || this.options.editable;
            return old_on_loaded.call(this, data, grouped);
        }
    });

    _.extend(openerp.base.ListView.Groups.prototype, /** @lends openerp.base.ListView.Groups# */{
        passtrough_events: openerp.base.ListView.Groups.prototype.passtrough_events + " edit saved",
        new_record: function () {
            // TODO: handle multiple children
            this.children[null].new_record();
        }
    });

    var old_list_row_clicked = openerp.base.ListView.List.prototype.row_clicked;
    _.extend(openerp.base.ListView.List.prototype, /** @lends openerp.base.ListView.List */{
        row_clicked: function (event) {
            if (!this.options.editable) {
                return old_list_row_clicked.call(this, event);
            }
            this.edit_record();
        },
        /**
         * Checks if a record is being edited, and if so cancels it
         */
        cancel_pending_edition: function () {
            if (!this.edition) {
                return;
            }

            if (this.edition_index !== null) {
                this.reload_record(this.edition_index);
            }
            this.edition_form.stop();
            this.edition_form.$element.remove();
            delete this.edition_form;
            delete this.edition_index;
            delete this.edition;
        },
        render_row_as_form: function (row) {
            this.cancel_pending_edition();

            var self = this;
            var $new_row = $('<tr>', {
                    id: _.uniqueId('oe-editable-row-'),
                    'class': $(row).attr('class'),
                    click: function (e) {e.stopPropagation();}
                })
                .delegate('button.oe-edit-row-save', 'click', function () {
                    self.save_row();
                })
                .delegate('button.oe-edit-row-cancel', 'click', function () {
                    self.cancel_edition();
                })
                .delegate('button', 'keyup', function (e) {
                    e.stopImmediatePropagation();
                })
                .keyup(function (e) {
                    switch (e.which) {
                        case KEY_RETURN:
                            self.save_row(true);
                            break;
                        case KEY_ESCAPE:
                            self.cancel_edition();
                            break;
                        default:
                            return;
                    }
                });
            if (row) {
                $new_row.replaceAll(row);
            } else if (this.options.editable === 'top') {
                this.$current.prepend($new_row);
            } else if (this.options.editable) {
                this.$current.append($new_row);
            }
            this.edition = true;
            this.edition_index = this.dataset.index;
            this.edition_form = _.extend(new openerp.base.FormView(
                    null, this.group.view.session, $new_row.attr('id'),
                    this.dataset, false), {
                template: 'ListView.row.form',
                registry: openerp.base.list.form.widgets
            });
            $.when(this.edition_form.on_loaded({fields_view: this.get_fields_view()})).then(function () {
                // put in $.when just in case  FormView.on_loaded becomes asynchronous
                $new_row.find('td')
                      .addClass('oe-field-cell')
                      .removeAttr('width')
                  .end()
                  .find('td:first').removeClass('oe-field-cell').end()
                  .find('td:last').removeClass('oe-field-cell').end();
                // pad in case of groupby
                _(self.columns).each(function (column) {
                    if (column.meta) {
                        $new_row.prepend('<td>');
                    }
                });

                self.edition_form.do_show();
            });
        },
        /**
         * Saves the current row, and triggers the edition of its following
         * sibling if asked.
         *
         * @param {Boolean} [edit_next=false] should the next row become editable
         */
        save_row: function (edit_next) {
            var self = this;
            this.edition_form.do_save(function (result) {
                self.reload_record(self.dataset.index, true).then(function () {
                    self.edition_form.stop();
                    delete self.edition_form;
                    delete self.edition_index;
                    delete self.edition;

                    $(self).trigger('saved', [self.dataset]);
                    if (!edit_next) {
                        return;
                    }
                    if (result.created) {
                        self.new_record();
                        return;
                    }
                    self.dataset.next();
                    self.edit_record();
                });
            }, this.options.editable === 'top');
        },
        /**
         * Cancels the edition of the row for the current dataset index
         */
        cancel_edition: function () {
            this.cancel_pending_edition();
        },
        /**
         * Edits record currently selected via dataset
         */
        edit_record: function () {
            this.render_row_as_form(
                this.$current.children(
                    _.sprintf('[data-index=%d]',
                            this.dataset.index)));
            $(this).trigger(
                'edit',
                [this.rows[this.dataset.index].data.id.value, this.dataset]);
        },
        new_record: function () {
            this.dataset.index = null;
            this.render_row_as_form();
        }
    });
    openerp.base.list = {form: {}};
    openerp.base.list.form.WidgetFrame = openerp.base.form.WidgetFrame.extend({
        template: 'ListView.row.frame'
    });
    var form_widgets = openerp.base.form.widgets;
    openerp.base.list.form.widgets = form_widgets.clone({
        'frame': 'openerp.base.list.form.WidgetFrame'
    });
    // All form widgets inherit a problematic behavior from
    // openerp.base.form.WidgetFrame: the cell itself is removed when invisible
    // whether it's @invisible or @attrs[invisible]. In list view, only the
    // former should completely remove the cell. We need to override update_dom
    // on all widgets since we can't just hit on widget itself (I think)
    var list_form_widgets = openerp.base.list.form.widgets;
    _(list_form_widgets.map).each(function (widget_path, key) {
        if (key === 'frame') { return; }
        var new_path = 'openerp.base.list.form.' + key;

        openerp.base.list.form[key] = (form_widgets.get_object(key)).extend({
            update_dom: function () {
                this.$element.children().css('visibility', '');
                if (this.invisible && this.node.attrs.invisible !== '1') {
                    this.$element.children().css('visibility', 'hidden');
                } else {
                    this._super();
                }
            }
        });
        list_form_widgets.add(key, new_path);
    });
};
<|endoftext|>


<commit_before>const mdxToJsx = require('@mdx-js/mdx/mdx-hast-to-jsx');
const parser = require('@babel/parser');
const generate = require('@babel/generator').default;
const camelCase = require('lodash/camelCase');
const jsStringEscape = require('js-string-escape');

// Generate the MDX as is, but append named exports for every
// story in the contents

const STORY_REGEX = /^<Story[\s>]/;
const PREVIEW_REGEX = /^<Preview[\s>]/;
const META_REGEX = /^<Meta[\s>]/;
const RESERVED = /^(?:do|if|in|for|let|new|try|var|case|else|enum|eval|false|null|this|true|void|with|await|break|catch|class|const|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$/;

function getAttr(elt, what) {
  const attr = elt.attributes.find(n => n.name.name === what);
  return attr && attr.value;
}

function getStoryFn(name, counter) {
  if (name) {
    const storyFn = camelCase(name.replace(/[^a-z0-9-]/g, '-'));
    if (storyFn.length > 1 && !RESERVED.exec(storyFn)) {
      return storyFn;
    }
  }
  return `story${counter}`;
}

function genStoryExport(ast, counter) {
  let storyName = getAttr(ast.openingElement, 'name');
  let storyId = getAttr(ast.openingElement, 'id');
  storyName = storyName && storyName.value;
  storyId = storyId && storyId.value;

  if (!storyId && !storyName) {
    throw new Error('Expected a story name or ID attribute');
  }

  // We don't generate exports for story references or the smart "current story"
  if (storyId || !storyName) {
    return null;
  }

  // console.log('genStoryExport', JSON.stringify(ast, null, 2));

  const statements = [];
  const storyKey = getStoryFn(storyName, counter);

  let body = ast.children.find(n => n.type !== 'JSXText');
  let storyCode = null;
  if (!body) {
    // plain text node
    const { code } = generate(ast.children[0], {});
    storyCode = `'${code}'`;
  } else {
    if (body.type === 'JSXExpressionContainer') {
      // FIXME: handle fragments
      body = body.expression;
    }
    const { code } = generate(body, {});
    storyCode = code;
  }
  if (storyCode.trim().startsWith('() =>')) {
    statements.push(`export const ${storyKey} = ${storyCode}`);
  } else {
    statements.push(
      `export const ${storyKey} = () => (
        ${storyCode}
      );`
    );
  }
  statements.push(`${storyKey}.story = {};`);

  if (storyName !== storyKey) {
    statements.push(`${storyKey}.story.name = '${storyName}';`);
  }

  let parameters = getAttr(ast.openingElement, 'parameters');
  parameters = parameters && parameters.expression;
  const source = jsStringEscape(storyCode);
  if (parameters) {
    const { code: params } = generate(parameters, {});
    // FIXME: hack in the story's source as a parameter
    statements.push(`${storyKey}.story.parameters = { mdxSource: '${source}', ...${params} };`);
  } else {
    statements.push(`${storyKey}.story.parameters = { mdxSource: '${source}' };`);
  }

  let decorators = getAttr(ast.openingElement, 'decorators');
  decorators = decorators && decorators.expression;
  if (decorators) {
    const { code: decos } = generate(decorators, {});
    statements.push(`${storyKey}.story.decorators = ${decos};`);
  }

  return {
    [storyKey]: statements.join('\n'),
  };
}

function genPreviewExports(ast, counter) {
  // console.log('genPreviewExports', JSON.stringify(ast, null, 2));

  let localCounter = counter;
  const previewExports = {};
  for (let i = 0; i < ast.children.length; i += 1) {
    const child = ast.children[i];
    if (child.type === 'JSXElement' && child.openingElement.name.name === 'Story') {
      const storyExport = genStoryExport(child, localCounter);
      if (storyExport) {
        Object.assign(previewExports, storyExport);
        localCounter += 1;
      }
    }
  }
  return previewExports;
}

function genMeta(ast) {
  let title = getAttr(ast.openingElement, 'title');
  let parameters = getAttr(ast.openingElement, 'parameters');
  let decorators = getAttr(ast.openingElement, 'decorators');
  title = title && `'${title.value}'`;
  if (parameters && parameters.expression) {
    const { code: params } = generate(parameters.expression, {});
    parameters = params;
  }
  if (decorators && decorators.expression) {
    const { code: decos } = generate(decorators.expression, {});
    decorators = decos;
  }
  return {
    title,
    parameters,
    decorators,
  };
}

function getExports(node, counter) {
  const { value, type } = node;
  if (type === 'jsx') {
    if (STORY_REGEX.exec(value)) {
      // Single story
      const ast = parser.parseExpression(value, { plugins: ['jsx'] });
      const storyExport = genStoryExport(ast, counter);
      return storyExport && { stories: storyExport };
    }
    if (PREVIEW_REGEX.exec(value)) {
      // Preview, possibly containing multiple stories
      const ast = parser.parseExpression(value, { plugins: ['jsx'] });
      return { stories: genPreviewExports(ast, counter) };
    }
    if (META_REGEX.exec(value)) {
      // Preview, possibly containing multiple stories
      const ast = parser.parseExpression(value, { plugins: ['jsx'] });
      return { meta: genMeta(ast) };
    }
  }
  return null;
}

// insert `mdxKind` into the context so that we can know what "kind" we're rendering into
// when we render <Story name="xxx">...</Story>, since this MDX can be attached to any `selectedKind`!
const wrapperJs = `
const mdxKind = componentMeta.title || componentMeta.displayName;
const WrappedMDXContent = ({ context }) => <DocsContainer context={{...context, mdxKind}} content={MDXContent} />;
componentMeta.parameters = componentMeta.parameters || {};
componentMeta.parameters.docs = WrappedMDXContent;
`.trim();

function stringifyMeta(meta) {
  let result = '{ ';
  Object.entries(meta).forEach(([key, val]) => {
    if (val) {
      result += `${key}: ${val}, `;
    }
  });
  result += ' }';
  return result;
}

function extractExports(node, options) {
  // we're overriding default export
  const defaultJsx = mdxToJsx.toJSX(node, {}, { ...options, skipExport: true });
  const storyExports = [];
  const includeStories = [];
  let metaExport = null;
  let counter = 0;
  node.children.forEach(n => {
    const exports = getExports(n, counter);
    if (exports) {
      const { stories, meta } = exports;
      if (stories) {
        Object.entries(stories).forEach(([key, story]) => {
          includeStories.push(key);
          storyExports.push(story);
          counter += 1;
        });
      }
      if (meta) {
        if (metaExport) {
          throw new Error('Meta can only be declared once');
        }
        metaExport = meta;
      }
    }
  });
  if (metaExport) {
    if (!storyExports.length) {
      storyExports.push('export const storybookDocsOnly = () => "See docs";');
      storyExports.push('storybookDocsOnly.story = { parameters: { docsOnly: true } };');
      includeStories.push('storybookDocsOnly');
    }
  } else {
    metaExport = {};
  }
  metaExport.includeStories = JSON.stringify(includeStories);

  const fullJsx = [
    'import { DocsContainer } from "@storybook/addon-docs/blocks";',
    defaultJsx,
    ...storyExports,
    `const componentMeta = ${stringifyMeta(metaExport)};`,
    wrapperJs,
    'export default componentMeta;',
  ].join('\n\n');

  return fullJsx;
}

function createCompiler(mdxOptions) {
  return function compiler(options = {}) {
    this.Compiler = tree => extractExports(tree, options, mdxOptions);
  };
}

module.exports = createCompiler;
<commit_msg>Addon-docs: Make docs-only stories throw an error on render<commit_after>const mdxToJsx = require('@mdx-js/mdx/mdx-hast-to-jsx');
const parser = require('@babel/parser');
const generate = require('@babel/generator').default;
const camelCase = require('lodash/camelCase');
const jsStringEscape = require('js-string-escape');

// Generate the MDX as is, but append named exports for every
// story in the contents

const STORY_REGEX = /^<Story[\s>]/;
const PREVIEW_REGEX = /^<Preview[\s>]/;
const META_REGEX = /^<Meta[\s>]/;
const RESERVED = /^(?:do|if|in|for|let|new|try|var|case|else|enum|eval|false|null|this|true|void|with|await|break|catch|class|const|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$/;

function getAttr(elt, what) {
  const attr = elt.attributes.find(n => n.name.name === what);
  return attr && attr.value;
}

function getStoryFn(name, counter) {
  if (name) {
    const storyFn = camelCase(name.replace(/[^a-z0-9-]/g, '-'));
    if (storyFn.length > 1 && !RESERVED.exec(storyFn)) {
      return storyFn;
    }
  }
  return `story${counter}`;
}

function genStoryExport(ast, counter) {
  let storyName = getAttr(ast.openingElement, 'name');
  let storyId = getAttr(ast.openingElement, 'id');
  storyName = storyName && storyName.value;
  storyId = storyId && storyId.value;

  if (!storyId && !storyName) {
    throw new Error('Expected a story name or ID attribute');
  }

  // We don't generate exports for story references or the smart "current story"
  if (storyId || !storyName) {
    return null;
  }

  // console.log('genStoryExport', JSON.stringify(ast, null, 2));

  const statements = [];
  const storyKey = getStoryFn(storyName, counter);

  let body = ast.children.find(n => n.type !== 'JSXText');
  let storyCode = null;
  if (!body) {
    // plain text node
    const { code } = generate(ast.children[0], {});
    storyCode = `'${code}'`;
  } else {
    if (body.type === 'JSXExpressionContainer') {
      // FIXME: handle fragments
      body = body.expression;
    }
    const { code } = generate(body, {});
    storyCode = code;
  }
  if (storyCode.trim().startsWith('() =>')) {
    statements.push(`export const ${storyKey} = ${storyCode}`);
  } else {
    statements.push(
      `export const ${storyKey} = () => (
        ${storyCode}
      );`
    );
  }
  statements.push(`${storyKey}.story = {};`);

  if (storyName !== storyKey) {
    statements.push(`${storyKey}.story.name = '${storyName}';`);
  }

  let parameters = getAttr(ast.openingElement, 'parameters');
  parameters = parameters && parameters.expression;
  const source = jsStringEscape(storyCode);
  if (parameters) {
    const { code: params } = generate(parameters, {});
    // FIXME: hack in the story's source as a parameter
    statements.push(`${storyKey}.story.parameters = { mdxSource: '${source}', ...${params} };`);
  } else {
    statements.push(`${storyKey}.story.parameters = { mdxSource: '${source}' };`);
  }

  let decorators = getAttr(ast.openingElement, 'decorators');
  decorators = decorators && decorators.expression;
  if (decorators) {
    const { code: decos } = generate(decorators, {});
    statements.push(`${storyKey}.story.decorators = ${decos};`);
  }

  return {
    [storyKey]: statements.join('\n'),
  };
}

function genPreviewExports(ast, counter) {
  // console.log('genPreviewExports', JSON.stringify(ast, null, 2));

  let localCounter = counter;
  const previewExports = {};
  for (let i = 0; i < ast.children.length; i += 1) {
    const child = ast.children[i];
    if (child.type === 'JSXElement' && child.openingElement.name.name === 'Story') {
      const storyExport = genStoryExport(child, localCounter);
      if (storyExport) {
        Object.assign(previewExports, storyExport);
        localCounter += 1;
      }
    }
  }
  return previewExports;
}

function genMeta(ast) {
  let title = getAttr(ast.openingElement, 'title');
  let parameters = getAttr(ast.openingElement, 'parameters');
  let decorators = getAttr(ast.openingElement, 'decorators');
  title = title && `'${title.value}'`;
  if (parameters && parameters.expression) {
    const { code: params } = generate(parameters.expression, {});
    parameters = params;
  }
  if (decorators && decorators.expression) {
    const { code: decos } = generate(decorators.expression, {});
    decorators = decos;
  }
  return {
    title,
    parameters,
    decorators,
  };
}

function getExports(node, counter) {
  const { value, type } = node;
  if (type === 'jsx') {
    if (STORY_REGEX.exec(value)) {
      // Single story
      const ast = parser.parseExpression(value, { plugins: ['jsx'] });
      const storyExport = genStoryExport(ast, counter);
      return storyExport && { stories: storyExport };
    }
    if (PREVIEW_REGEX.exec(value)) {
      // Preview, possibly containing multiple stories
      const ast = parser.parseExpression(value, { plugins: ['jsx'] });
      return { stories: genPreviewExports(ast, counter) };
    }
    if (META_REGEX.exec(value)) {
      // Preview, possibly containing multiple stories
      const ast = parser.parseExpression(value, { plugins: ['jsx'] });
      return { meta: genMeta(ast) };
    }
  }
  return null;
}

// insert `mdxKind` into the context so that we can know what "kind" we're rendering into
// when we render <Story name="xxx">...</Story>, since this MDX can be attached to any `selectedKind`!
const wrapperJs = `
const mdxKind = componentMeta.title || componentMeta.displayName;
const WrappedMDXContent = ({ context }) => <DocsContainer context={{...context, mdxKind}} content={MDXContent} />;
componentMeta.parameters = componentMeta.parameters || {};
componentMeta.parameters.docs = WrappedMDXContent;
`.trim();

function stringifyMeta(meta) {
  let result = '{ ';
  Object.entries(meta).forEach(([key, val]) => {
    if (val) {
      result += `${key}: ${val}, `;
    }
  });
  result += ' }';
  return result;
}

function extractExports(node, options) {
  // we're overriding default export
  const defaultJsx = mdxToJsx.toJSX(node, {}, { ...options, skipExport: true });
  const storyExports = [];
  const includeStories = [];
  let metaExport = null;
  let counter = 0;
  node.children.forEach(n => {
    const exports = getExports(n, counter);
    if (exports) {
      const { stories, meta } = exports;
      if (stories) {
        Object.entries(stories).forEach(([key, story]) => {
          includeStories.push(key);
          storyExports.push(story);
          counter += 1;
        });
      }
      if (meta) {
        if (metaExport) {
          throw new Error('Meta can only be declared once');
        }
        metaExport = meta;
      }
    }
  });
  if (metaExport) {
    if (!storyExports.length) {
      storyExports.push(
        'export const storybookDocsOnly = () => { throw new Error("Docs-only story"); };'
      );
      storyExports.push('storybookDocsOnly.story = { parameters: { docsOnly: true } };');
      includeStories.push('storybookDocsOnly');
    }
  } else {
    metaExport = {};
  }
  metaExport.includeStories = JSON.stringify(includeStories);

  const fullJsx = [
    'import { DocsContainer } from "@storybook/addon-docs/blocks";',
    defaultJsx,
    ...storyExports,
    `const componentMeta = ${stringifyMeta(metaExport)};`,
    wrapperJs,
    'export default componentMeta;',
  ].join('\n\n');

  return fullJsx;
}

function createCompiler(mdxOptions) {
  return function compiler(options = {}) {
    this.Compiler = tree => extractExports(tree, options, mdxOptions);
  };
}

module.exports = createCompiler;
<|endoftext|>