<commit_before>"use strict"; // strict mode syntax
var grid = null;
// initial table properties, after getting the data from the file.
var loadSlickTable = function(fileData){
	var c1 = document.getElementById('slickTable');
	var data = [];
	var columns = [];
	var w = 66 //default width
	//now we push the values of the columns into the grid
	for(var i = 0; i < fileData[0].length; i++){
		columns.push({
			id: i,
			name: '',
			field: i,
			width: w,
			editor: Slick.Editors.Text,
			minWidth: 130, //sets range for width of columns
			maxWidth: 130
		});
	}

  // set options for the table
  var options = {
	editable: true, //allows the table to be edited
    enableCellNavigation: true, //tabbable
    enableColumnReorder: false,
	autoHeight: true, //these three just make the
	forceFitColumns: true, //table look nice/fit
	fullWidthRows: true,
	rowHeight: 28
  };

  $(function () {
	// adding in the data to the grid
    for (var i = 0; i < fileData.length; i++) {
			var d = (data[i] = {});
			d["id"] = i;
			for(var j = 0; j < columns.length; j++){
				if(i > 0 && j > 0){
						d[j] = parseFloat(fileData[i][j]);
				}
				else {
					d[j] = fileData[i][j];
				}
			}
	}

	//Dynamic container width
	var container = document.getElementById('tblContainer');
	var cwidthNum;
    if (fileData[0].length < 6)
    	cwidthNum = fileData[0].length * 130; // max/min width (130)
    else if (fileData[0].length >= 6)
    	cwidthNum = 780;
    var cwidthString = cwidthNum + "px";
   	container.setAttribute("style", "width:" + cwidthString);

   	//Grid creation
    grid = new Slick.Grid(c1, data, columns, options);

  });
  return grid;
}

var linkSlickTable = function(chart, player, overlay, summary){
	grid.onCellChange.subscribe(function (e, args) {
		//args = Object {row: (row #) , cell: (column #), item: Object, grid: SlickGrid}
		var row = args.row;
		var col = args.cell;
		var newVal = grid.getData()[row][col];

		// if a label
		if (row == 0 || col == 0){
			chart.scale.xLabels[col - 1] = newVal;
			if (col === 0) {
				grid.getData()[0][0] = " ";
				//console.log("Hi there");
			}
			setTimeout(function(){ checkRemove(); }, 1);
		}

		// not a label - check to see if it's a number.
		// If not, do nothing
		else if ((!isNaN(newVal)) && (newVal != "")){
			newVal = parseFloat(newVal);
			//Update audio with new value
			player.changeLine(row-1,col - 1,newVal);
			// change value in chart
			if(type ==="bar")
				chart.datasets[row-1].bars[col - 1].value = newVal;
			else
				chart.datasets[row-1].points[col - 1].value = newVal;
		}
		else {
			var oldVal = chart.datasets[row-1].points[col - 1].value;
			grid.getData()[row][col] = oldVal;
			console.log(grid.getData()[row][col]);
		}

		//update chart and overlay
		chart.update();
		summary.update();
	});
}

//Hides slick grid header bars
function fixSlick(){
	var e = document.getElementById('slickTable').firstChild.nextSibling.firstChild;
	var c = e.firstChild;
	//Loop through slickgrid headers
	while(c){
		c.style.display="none";
		c = c.nextSibling;
	}
}

/*Checks first elements of rows and columns to see if there are any
* blank labels, indicating removal.
*/
function checkRemove(){
	var k = 0;
	//Get number of keys in grid data item
	for(var key in grid.getData()[0]){
		if(key != "id")
		k++;
	}
	//Check for columns to remove
	for(var i = 1; i < k; i++){
		if(grid.getData()[0][i]===""){
			if(confirm("Delete column " + i + "?")){	//Confirm with user
				removeColumns(i,1);	//Remove is confirmed
				if(grid.getCellNode(0,i)!= null)
					grid.gotoCell(0,i);
				else{
					grid.gotoCell(0,i-1);
				}
			}
			else
				grid.getData()[0][i] = i;	//Set back to default value if not
		}
	}
	//Check for rows to remove
	for(var i = 0; i < grid.getData().length; i++){
		if(grid.getData()[i][0]===""){
			if(confirm("Delete row " + i + "?")){	//Confirm with user
				removeRows(i,1);	//Remove if confirmed
				if(grid.getCellNode(i,0)!= null)
					grid.gotoCell(i,0);
				else{
					grid.gotoCell(i-1,0);
				}
			}
			else
				grid.getData()[i][0] = i;	//Set back to default value if not
		}
	}
	document.getElementById('tblContainer').style.width="100%";
}
<commit_msg>More easily change the Column Width of SlickTable<commit_after>"use strict"; // strict mode syntax
var grid = null;

var ColumnWidth = 130; //** CHANGE THIS TO CHANGE COLUMN WIDTH! **

// initial table properties, after getting the data from the file.
var loadSlickTable = function(fileData){
	var c1 = document.getElementById('slickTable');
	var data = [];
	var columns = [];
	var w = 66 //default width
	//now we push the values of the columns into the grid
	for(var i = 0; i < fileData[0].length; i++){
		columns.push({
			id: i,
			name: '',
			field: i,
			width: ColumnWidth,
			editor: Slick.Editors.Text,
			minWidth: ColumnWidth, //sets range for width of columns
			maxWidth: ColumnWidth
		});
	}

  // set options for the table
  var options = {
	editable: true, //allows the table to be edited
    enableCellNavigation: true, //tabbable
    enableColumnReorder: false,
	autoHeight: true, //these three just make the
	forceFitColumns: true, //table look nice/fit
	fullWidthRows: true,
	rowHeight: 28
  };

  $(function () {
	// adding in the data to the grid
    for (var i = 0; i < fileData.length; i++) {
			var d = (data[i] = {});
			d["id"] = i;
			for(var j = 0; j < columns.length; j++){
				if(i > 0 && j > 0){
						d[j] = parseFloat(fileData[i][j]);
				}
				else {
					d[j] = fileData[i][j];
				}
			}
	}

	//Dynamic Container Width
	//NOTE: To change the Column Width of the table please change "ColumnWidth" at the top of the file!
	var container = document.getElementById('tblContainer');
	var maxCol = Math.floor(800/ColumnWidth); //800 is maximum acceptable size for the table's container
	var maxContainerWidthNum = ColumnWidth * maxCol;
	var ContainerWidthNum;

	//Logic for Dynamic Container Width
    if (fileData[0].length < maxCol)
    	ContainerWidthNum = fileData[0].length * ColumnWidth; // max/min width (130)
    else if (fileData[0].length >= maxCol)
    	ContainerWidthNum = maxContainerWidthNum;
    var ContainerWidthString = ContainerWidthNum + "px";
   	container.setAttribute("style", "width:" + ContainerWidthString);

   	//Grid creation
    grid = new Slick.Grid(c1, data, columns, options);

  });
  return grid;
}

var linkSlickTable = function(chart, player, overlay, summary){
	grid.onCellChange.subscribe(function (e, args) {
		//args = Object {row: (row #) , cell: (column #), item: Object, grid: SlickGrid}
		var row = args.row;
		var col = args.cell;
		var newVal = grid.getData()[row][col];

		// if a label
		if (row == 0 || col == 0){
			chart.scale.xLabels[col - 1] = newVal;
			if (col === 0) {
				grid.getData()[0][0] = " ";
				//console.log("Hi there");
			}
			setTimeout(function(){ checkRemove(); }, 1);
		}

		// not a label - check to see if it's a number.
		// If not, do nothing
		else if ((!isNaN(newVal)) && (newVal != "")){
			newVal = parseFloat(newVal);
			//Update audio with new value
			player.changeLine(row-1,col - 1,newVal);
			// change value in chart
			if(type ==="bar")
				chart.datasets[row-1].bars[col - 1].value = newVal;
			else
				chart.datasets[row-1].points[col - 1].value = newVal;
		}
		else {
			var oldVal = chart.datasets[row-1].points[col - 1].value;
			grid.getData()[row][col] = oldVal;
			console.log(grid.getData()[row][col]);
		}

		//update chart and overlay
		chart.update();
		summary.update();
	});
}

//Hides slick grid header bars
function fixSlick(){
	var e = document.getElementById('slickTable').firstChild.nextSibling.firstChild;
	var c = e.firstChild;
	//Loop through slickgrid headers
	while(c){
		c.style.display="none";
		c = c.nextSibling;
	}
}

/*Checks first elements of rows and columns to see if there are any
* blank labels, indicating removal.
*/
function checkRemove(){
	var k = 0;
	//Get number of keys in grid data item
	for(var key in grid.getData()[0]){
		if(key != "id")
		k++;
	}
	//Check for columns to remove
	for(var i = 1; i < k; i++){
		if(grid.getData()[0][i]===""){
			if(confirm("Delete column " + i + "?")){	//Confirm with user
				removeColumns(i,1);	//Remove is confirmed
				if(grid.getCellNode(0,i)!= null)
					grid.gotoCell(0,i);
				else{
					grid.gotoCell(0,i-1);
				}
			}
			else
				grid.getData()[0][i] = i;	//Set back to default value if not
		}
	}
	//Check for rows to remove
	for(var i = 0; i < grid.getData().length; i++){
		if(grid.getData()[i][0]===""){
			if(confirm("Delete row " + i + "?")){	//Confirm with user
				removeRows(i,1);	//Remove if confirmed
				if(grid.getCellNode(i,0)!= null)
					grid.gotoCell(i,0);
				else{
					grid.gotoCell(i-1,0);
				}
			}
			else
				grid.getData()[i][0] = i;	//Set back to default value if not
		}
	}
	document.getElementById('tblContainer').style.width="100%";
}
<|endoftext|>


<commit_before>/**
 * @ngdoc service
 * @name umbraco.services.localizationService
 *
 * @requires $http
 * @requires $q
 * @requires $window
 * @requires $filter
 *
 * @description
 * Application-wide service for handling localization
 *
 * ##usage
 * To use, simply inject the localizationService into any controller that needs it, and make
 * sure the umbraco.services module is accesible - which it should be by default.
 *
 *

 *    localizationService.localize("area_key").then(function(value){
 *        element.html(value);
 *    });
 *

 */

angular.module('umbraco.services')
.factory('localizationService', function ($http, $q, eventsService, $window, $filter, userService) {

    //TODO: This should be injected as server vars
    var url = "LocalizedText";
    var resourceFileLoadStatus = "none";
    var resourceLoadingPromise = [];

    function _lookup(value, tokens, dictionary) {

        //strip the key identifier if its there
        if (value && value[0] === "@") {
            value = value.substring(1);
        }

        //if no area specified, add general_
        if (value && value.indexOf("_") < 0) {
            value = "general_" + value;
        }

        var entry = dictionary[value];
        if (entry) {
            if (tokens) {
                for (var i = 0; i < tokens.length; i++) {
                    entry = entry.replace("%" + i + "%", tokens[i]);
                }
            }
            return entry;
        }
        return "[" + value + "]";
    }

    var service = {
        // array to hold the localized resource string entries
        dictionary: [],

        // loads the language resource file from the server
        initLocalizedResources: function () {
            var deferred = $q.defer();

            if (resourceFileLoadStatus === "loaded") {
                deferred.resolve(service.dictionary);
                return deferred.promise;
            }

            //if the resource is already loading, we don't want to force it to load another one in tandem, we'd rather
            // wait for that initial http promise to finish and then return this one with the dictionary loaded
            if (resourceFileLoadStatus === "loading") {
                //add to the list of promises waiting
                resourceLoadingPromise.push(deferred);

                //exit now it's already loading
                return deferred.promise;
            }

            resourceFileLoadStatus = "loading";

            // build the url to retrieve the localized resource file
            $http({ method: "GET", url: url, cache: false })
                .then(function (response) {
                    resourceFileLoadStatus = "loaded";
                    service.dictionary = response.data;

                    eventsService.emit("localizationService.updated", response.data);

                    deferred.resolve(response.data);
                    //ensure all other queued promises are resolved
                    for (var p in resourceLoadingPromise) {
                        resourceLoadingPromise[p].resolve(response.data);
                    }
                }, function (err) {
                    deferred.reject("Something broke");
                    //ensure all other queued promises are resolved
                    for (var p in resourceLoadingPromise) {
                        resourceLoadingPromise[p].reject("Something broke");
                    }
                });
            return deferred.promise;
        },

        /**
         * @ngdoc method
         * @name umbraco.services.localizationService#tokenize
         * @methodOf umbraco.services.localizationService
         *
         * @description
         * Helper to tokenize and compile a localization string
         * @param {String} value the value to tokenize
         * @param {Object} scope the $scope object
         * @returns {String} tokenized resource string
         */
        tokenize: function (value, scope) {
            if (value) {
                var localizer = value.split(':');
                var retval = { tokens: undefined, key: localizer[0].substring(0) };
                if (localizer.length > 1) {
                    retval.tokens = localizer[1].split(',');
                    for (var x = 0; x < retval.tokens.length; x++) {
                        retval.tokens[x] = scope.$eval(retval.tokens[x]);
                    }
                }

                return retval;
            }
            return value;
        },

        /**
         * @ngdoc method
         * @name umbraco.services.localizationService#localize
         * @methodOf umbraco.services.localizationService
         *
         * @description
         * Checks the dictionary for a localized resource string
         * @param {String} value the area/key to localize in the format of 'section_key'
         * alternatively if no section is set such as 'key' then we assume the key is to be looked in
         * the 'general' section
         *
         * @param {Array} tokens if specified this array will be sent as parameter values
         * This replaces %0% and %1% etc in the dictionary key value with the passed in strings
         *
         * @returns {String} localized resource string
         */
        localize: function (value, tokens) {
            return service.initLocalizedResources().then(function (dic) {
                var val = _lookup(value, tokens, dic);
                return val;
            });
        },

        localizeMany: function(keys) {
            if(keys){

                //The LocalizationService.localize promises we want to resolve
                var promises = [];

                for(var i = 0; i < keys.length; i++){
                    promises.push(service.localize(keys[i], undefined));
                }

                return $q.all(promises).then(function(localizedValues){
                    return localizedValues;
                });
            }
        },

        concat: function(keys) {
            if(keys){

                //The LocalizationService.localize promises we want to resolve
                var promises = [];

                for(var i = 0; i < keys.length; i++){
                    promises.push(service.localize(keys[i], undefined));
                }

                return $q.all(promises).then(function(localizedValues){

                    //Build a concat string by looping over the array of resolved promises/translations
                    var returnValue = "";

                    for(var i = 0; i < localizedValues.length; i++){
                        returnValue += localizedValues[i];
                    }

                    return returnValue;
                });
            }
        }

    };

    //This happens after login / auth and assets loading
    eventsService.on("app.authenticated", function () {
        resourceFileLoadStatus = "none";
        resourceLoadingPromise = [];
    });

    // return the local instance when called
    return service;
});
<commit_msg>Adds in new format function to localizationService simialr to C# String.Format() usign %0% as tokens. Documents the new functions added.<commit_after>/**
 * @ngdoc service
 * @name umbraco.services.localizationService
 *
 * @requires $http
 * @requires $q
 * @requires $window
 * @requires $filter
 *
 * @description
 * Application-wide service for handling localization
 *
 * ##usage
 * To use, simply inject the localizationService into any controller that needs it, and make
 * sure the umbraco.services module is accesible - which it should be by default.
 *
 *

 *    localizationService.localize("area_key").then(function(value){
 *        element.html(value);
 *    });
 *

 */

angular.module('umbraco.services')
.factory('localizationService', function ($http, $q, eventsService, $window, $filter, userService) {

    //TODO: This should be injected as server vars
    var url = "LocalizedText";
    var resourceFileLoadStatus = "none";
    var resourceLoadingPromise = [];

    function _lookup(value, tokens, dictionary) {

        //strip the key identifier if its there
        if (value && value[0] === "@") {
            value = value.substring(1);
        }

        //if no area specified, add general_
        if (value && value.indexOf("_") < 0) {
            value = "general_" + value;
        }

        var entry = dictionary[value];
        if (entry) {
            if (tokens) {
                for (var i = 0; i < tokens.length; i++) {
                    entry = entry.replace("%" + i + "%", tokens[i]);
                }
            }
            return entry;
        }
        return "[" + value + "]";
    }

    var service = {
        // array to hold the localized resource string entries
        dictionary: [],

        // loads the language resource file from the server
        initLocalizedResources: function () {
            var deferred = $q.defer();

            if (resourceFileLoadStatus === "loaded") {
                deferred.resolve(service.dictionary);
                return deferred.promise;
            }

            //if the resource is already loading, we don't want to force it to load another one in tandem, we'd rather
            // wait for that initial http promise to finish and then return this one with the dictionary loaded
            if (resourceFileLoadStatus === "loading") {
                //add to the list of promises waiting
                resourceLoadingPromise.push(deferred);

                //exit now it's already loading
                return deferred.promise;
            }

            resourceFileLoadStatus = "loading";

            // build the url to retrieve the localized resource file
            $http({ method: "GET", url: url, cache: false })
                .then(function (response) {
                    resourceFileLoadStatus = "loaded";
                    service.dictionary = response.data;

                    eventsService.emit("localizationService.updated", response.data);

                    deferred.resolve(response.data);
                    //ensure all other queued promises are resolved
                    for (var p in resourceLoadingPromise) {
                        resourceLoadingPromise[p].resolve(response.data);
                    }
                }, function (err) {
                    deferred.reject("Something broke");
                    //ensure all other queued promises are resolved
                    for (var p in resourceLoadingPromise) {
                        resourceLoadingPromise[p].reject("Something broke");
                    }
                });
            return deferred.promise;
        },

        /**
         * @ngdoc method
         * @name umbraco.services.localizationService#tokenize
         * @methodOf umbraco.services.localizationService
         *
         * @description
         * Helper to tokenize and compile a localization string
         * @param {String} value the value to tokenize
         * @param {Object} scope the $scope object
         * @returns {String} tokenized resource string
         */
        tokenize: function (value, scope) {
            if (value) {
                var localizer = value.split(':');
                var retval = { tokens: undefined, key: localizer[0].substring(0) };
                if (localizer.length > 1) {
                    retval.tokens = localizer[1].split(',');
                    for (var x = 0; x < retval.tokens.length; x++) {
                        retval.tokens[x] = scope.$eval(retval.tokens[x]);
                    }
                }

                return retval;
            }
            return value;
        },

        /**
         * @ngdoc method
         * @name umbraco.services.localizationService#localize
         * @methodOf umbraco.services.localizationService
         *
         * @description
         * Checks the dictionary for a localized resource string
         * @param {String} value the area/key to localize in the format of 'section_key'
         * alternatively if no section is set such as 'key' then we assume the key is to be looked in
         * the 'general' section
         *
         * @param {Array} tokens if specified this array will be sent as parameter values
         * This replaces %0% and %1% etc in the dictionary key value with the passed in strings
         *
         * @returns {String} localized resource string
         */
        localize: function (value, tokens) {
            return service.initLocalizedResources().then(function (dic) {
                var val = _lookup(value, tokens, dic);
                return val;
            });
        },

        /**
         * @ngdoc method
         * @name umbraco.services.localizationService#localizeMany
         * @methodOf umbraco.services.localizationService
         *
         * @description
         * Checks the dictionary for multipe localized resource strings at once, preventing the need for nested promises
         * with localizationService.localize
         *
         * ##Usage
         *

         * localizationService.localizeMany(["speechBubbles_templateErrorHeader", "speechBubbles_templateErrorText"]).then(function(data){
         *      var header = data[0];
         *      var message = data[1];
         *      notificationService.error(header, message);
         * });
         *

         *
         * @param {Array} keys is an array of strings of the area/key to localize in the format of 'section_key'
         * alternatively if no section is set such as 'key' then we assume the key is to be looked in
         * the 'general' section
         *
         * @returns {Array} An array of localized resource string in the same order
         */
        localizeMany: function(keys) {
            if(keys){

                //The LocalizationService.localize promises we want to resolve
                var promises = [];

                for(var i = 0; i < keys.length; i++){
                    promises.push(service.localize(keys[i], undefined));
                }

                return $q.all(promises).then(function(localizedValues){
                    return localizedValues;
                });
            }
        },

        /**
         * @ngdoc method
         * @name umbraco.services.localizationService#concat
         * @methodOf umbraco.services.localizationService
         *
         * @description
         * Checks the dictionary for multipe localized resource strings at once & concats them to a single string
         * Which was not possible with localizationSerivce.localize() due to returning a promise
         *
         * ##Usage
         *

         * localizationService.concat(["speechBubbles_templateErrorHeader", "speechBubbles_templateErrorText"]).then(function(data){
         *      var combinedText = data;
         * });
         *

         *
         * @param {Array} keys is an array of strings of the area/key to localize in the format of 'section_key'
         * alternatively if no section is set such as 'key' then we assume the key is to be looked in
         * the 'general' section
         *
         * @returns {String} An concatenated string of localized resource string passed into the function in the same order
         */
        concat: function(keys) {
            if(keys){

                //The LocalizationService.localize promises we want to resolve
                var promises = [];

                for(var i = 0; i < keys.length; i++){
                    promises.push(service.localize(keys[i], undefined));
                }

                return $q.all(promises).then(function(localizedValues){

                    //Build a concat string by looping over the array of resolved promises/translations
                    var returnValue = "";

                    for(var i = 0; i < localizedValues.length; i++){
                        returnValue += localizedValues[i];
                    }

                    return returnValue;
                });
            }
        },

        /**
         * @ngdoc method
         * @name umbraco.services.localizationService#format
         * @methodOf umbraco.services.localizationService
         *
         * @description
         * Checks the dictionary for multipe localized resource strings at once & formats a tokenized message
         * Which was not possible with localizationSerivce.localize() due to returning a promise
         *
         * ##Usage
         *

         * localizationService.format(["template_insert", "template_insertSections"], "%0% %1%").then(function(data){
         *      //Will return 'Insert Sections'
         *      var formattedResult = data;
         * });
         *

         *
         * @param {Array} keys is an array of strings of the area/key to localize in the format of 'section_key'
         * alternatively if no section is set such as 'key' then we assume the key is to be looked in
         * the 'general' section
         *
         * @param {String} message is the string you wish to replace containing tokens in the format of %0% and %1%
         * with the localized resource strings
         *
         * @returns {String} An concatenated string of localized resource string passed into the function in the same order
         */
        format: function(keys, message){
            if(keys){

                //The LocalizationService.localize promises we want to resolve
                var promises = [];

                for(var i = 0; i < keys.length; i++){
                    promises.push(service.localize(keys[i], undefined));
                }

                return $q.all(promises).then(function(localizedValues){

                    //Replace {0} and {1} etc in message with the localized values
                    for(var i = 0; i < localizedValues.length; i++){
                        var token = "%" + i + "%";
                        var regex = new RegExp(token, "g");

                        message = message.replace(regex, localizedValues[i]);
                    }

                    return message;
                });
            }
        }

    };

    //This happens after login / auth and assets loading
    eventsService.on("app.authenticated", function () {
        resourceFileLoadStatus = "none";
        resourceLoadingPromise = [];
    });

    // return the local instance when called
    return service;
});
<|endoftext|>


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

    function momenthelperService($q, $http, umbRequestHelper) {

        var existingLocales = [];

        function getSupportedLocales() {
            var deferred = $q.defer();

            if (existingLocales.length === 0) {
                umbRequestHelper.resourcePromise(
                    $http.get(
                        umbRequestHelper.getApiUrl(
                            "momentApiBaseUrl",
                            "GetSupportedLocales")),
                    'Failed to get cultures').then(function(locales) {
                    existingLocales = locales;
                    deferred.resolve(existingLocales);
                });
            } else {
                deferred.resolve(existingLocales);
            }

            return deferred.promise;
        }

        ////////////

        var service = {
            getSupportedLocales: getSupportedLocales
        };

        return service;

    }

    angular.module('umbraco.services').factory('momenthelperService', momenthelperService);

})();

<commit_msg>Removes ugly non-existing comment :)<commit_after>(function () {
    'use strict';

    function momenthelperService($q, $http, umbRequestHelper) {

        var existingLocales = [];

        function getSupportedLocales() {
            var deferred = $q.defer();

            if (existingLocales.length === 0) {
                umbRequestHelper.resourcePromise(
                    $http.get(
                        umbRequestHelper.getApiUrl(
                            "momentApiBaseUrl",
                            "GetSupportedLocales")),
                    'Failed to get cultures').then(function(locales) {
                    existingLocales = locales;
                    deferred.resolve(existingLocales);
                });
            } else {
                deferred.resolve(existingLocales);
            }

            return deferred.promise;
        }

        var service = {
            getSupportedLocales: getSupportedLocales
        };

        return service;

    }

    angular.module('umbraco.services').factory('momenthelperService', momenthelperService);

})();

<|endoftext|>


<commit_before>/**
 * Created by ksinger on 24.08.2015.
 */
import angular from "angular";

import "ng-redux";
import won from "../won-es6.js";
import { postTitleCharacterLimit } from "config";
import { attach, deepFreeze, clone, dispatchEvent } from "../utils.js";
import { actionCreators } from "../actions/actions.js";
import { connect2Redux } from "../won-utils.js";

const emptyDraft = deepFreeze({
  title: "",
  type: won.WON.BasicNeedTypeCombined,
  description: "",
  tags: [],
  location: undefined,
  fromLocation: undefined,
  toLocation: undefined,
  thumbnail: undefined,
  matchingContext: undefined,
});

// const availableDetails = {
//   description: {},
//   location : {
//     detailName: "location",
//     detailTitle: "Location",
//     detailIcon: "ico36_location_circle",
//     detailComponent: "won-location-picker",
//   },
//   tags: {},
//   ttl: {},
// };

//TODO: can't inject $scope with the angular2-router, preventing redux-cleanup
const serviceDependencies = [
  "$ngRedux",
  "$scope",
  "$element" /*, '$routeParams' /*injections as strings here*/,
];

function genComponentConf() {
  const template = `

            <input
                type="text"
                maxlength="{{self.characterLimit}}"
                class="cis__mandatory__title won-txt"
                placeholder="What? - Short title shown in lists"
                ng-blur="::self.updateTitle()"
                ng-keyup="::self.updateTitle()"/>

                    Add more detail
                    <svg class="cis__addDetail__header__carret" ng-show="!self.showDetail">
                        <use xlink:href="#ico16_arrow_down" href="#ico16_arrow_down"></use>
                    </svg>
                    <svg class="cis__addDetail__header__carret" ng-show="self.showDetail">
                        <use xlink:href="#ico16_arrow_up" href="#ico16_arrow_up"></use>
                    </svg>

                        <svg class="cis__circleicon" ng-show="!self.details.has('description')">
                            <use xlink:href="#ico36_description_circle" href="#ico36_description_circle"></use>
                        </svg>
                        <svg class="cis__circleicon" ng-show="self.details.has('description')">
                            <use xlink:href="#ico36_added_circle" href="#ico36_added_circle"></use>
                        </svg>
                        Description

                        <svg class="cis__circleicon" ng-show="!self.details.has('location')">
                            <use xlink:href="#ico36_location_circle" href="#ico36_location_circle"></use>
                        </svg>
                        <svg class="cis__circleicon" ng-show="self.details.has('location')">
                            <use xlink:href="#ico36_added_circle" href="#ico36_added_circle"></use>
                        </svg>
                        Location

                        <svg class="cis__circleicon" ng-show="!self.details.has('route')">
                            <use xlink:href="#ico36_location_circle" href="#ico36_location_circle"></use>
                        </svg>
                        <svg class="cis__circleicon" ng-show="self.details.has('route')">
                            <use xlink:href="#ico36_added_circle" href="#ico36_added_circle"></use>
                        </svg>
                        Route (From - To)

                        <svg class="cis__circleicon" ng-show="!self.details.has('tags')">
                            <use xlink:href="#ico36_tags_circle" href="#ico36_tags_circle"></use>
                        </svg>
                        <svg class="cis__circleicon" ng-show="self.details.has('tags')">
                            <use xlink:href="#ico36_added_circle" href="#ico36_added_circle"></use>
                        </svg>
                        Tags

                        <svg class="cis__circleicon" ng-show="!self.details.has('ttl')">
                            <use xlink:href="#ico36_rdf_logo_circle" href="#ico36_rdf_logo_circle"></use>
                        </svg>
                        <svg class="cis__circleicon" ng-show="self.details.has('ttl')">
                            <use xlink:href="#ico36_added_circle" href="#ico36_added_circle"></use>
                        </svg>
                        Turtle (TTL)

            <won-description-picker
              ng-if="self.openDetail === 'description'"
              initial-description="::self.draftObject.description"
              on-description-updated="::self.updateDescription(description)">
            </won-description-picker>

            <won-location-picker
                ng-if="self.openDetail === 'location'"
                initial-location="::self.draftObject.location"
                on-location-picked="::self.updateLocation(location)">
            </won-location-picker>

            <won-route-picker
              ng-if="self.openDetail === 'route'"
              initial-fromLocation="::self.draftObject.fromLcoation"
              initial-toLocation="::self.draftObject.toLocation"
              on-route-updated="::self.updateRoute(fromLocation, toLocation)">
            </won-route-picker>

            <won-tags-picker
                ng-if="self.openDetail === 'tags'"
                initial-tags="::self.draftObject.tags"
                on-tags-updated="::self.updateTags(tags)">
            </won-tags-picker>

            <won-ttl-picker
              ng-if="self.openDetail === 'ttl'"
              initial-ttl="::self.draftObject.ttl"
              on-ttl-updated="::self.updateTTL(ttl)">
            </won-ttl-picker>

`;

  class Controller {
    constructor(/* arguments <- serviceDependencies */) {
      attach(this, serviceDependencies, arguments);
      this.won = won;

      //TODO: debug; deleteme
      window.cis4dbg = this;

      this.characterLimit = postTitleCharacterLimit;

      this.openDetail = undefined;

      this.reset();

      const selectFromState = () => ({});

      // Using actionCreators like this means that every action defined there is available in the template.
      connect2Redux(selectFromState, actionCreators, [], this);
    }

    reset() {
      this.draftObject = clone(emptyDraft);
      this.details = new Set(); // remove all detail-cards

      this.showDetail = false; // and close selector
    }

    updateDraft() {
      if (!this.details.has("location")) {
        this.draftObject.location = undefined;
      }
      if (!this.details.has("tags")) {
        this.draftObject.tags = [];
      }
      if (!this.details.has("ttl")) {
        this.draftObject.ttl = undefined;
      }
      if (!this.details.has("description")) {
        this.draftObject.description = undefined;
      }
      if (!this.description.has("route")) {
        this.draftObject.fromLocation = undefined;
        this.draftObject.toLocation = undefined;
      }

      this.onUpdate({ draft: this.draftObject });
      dispatchEvent(this.$element[0], "update", { draft: this.draftObject });
    }

    setDraft(updatedDraft) {
      Object.assign(this.draftObject, updatedDraft);
      this.updateDraft();
    }

    updateTitle() {
      const titleString = (this.titleInput() || {}).value || "";
      this.draftObject.title = titleString;
      this.updateDraft();
    }

    updateDescription(description) {
      console.log("description updated with: ", description);
      if (description && description.length > 0) {
        if (!this.details.has("description")) {
          this.details.add("description");
        }
        this.draftObject.description = description;
      } else if (this.details.has("description")) {
        this.details.delete("description");
        this.draftObject.description = undefined;
      }
    }

    updateLocation(location) {
      if (location) {
        if (!this.details.has("location")) {
          this.details.add("location");
        }
        this.draftObject.location = location;
      } else if (this.details.has("location")) {
        this.details.delete("location");
        this.draftObject.location = undefined;
      }

      this.updateDraft();
    }

    updateTags(tags) {
      if (tags && tags.length > 0) {
        if (!this.details.has("tags")) {
          this.details.add("tags");
        }
        this.draftObject.tags = tags;
      } else if (this.details.has("tags")) {
        this.details.delete("tags");
        this.draftObject.tags = [];
      }

      this.updateDraft();
    }

    updateTTL(ttl) {
      if (ttl && ttl.length > 0) {
        if (!this.details.has("ttl")) {
          this.details.add("ttl");
        }
        this.draftObject.ttl = ttl;
      } else if (this.details.has("ttl")) {
        this.details.delete("ttl");
        this.draftObject.ttl = undefined;
      }

      this.updateDraft();
    }

    pickImage(image) {
      this.draftObject.thumbnail = image;
    }

    toggleDetail() {
      this.showDetail = !this.showDetail;
    }

    toggleOpenDetail(detail) {
      // open clicked detail
      if (this.openDetail === detail) {
        this.openDetail = undefined;
      } else {
        this.openDetail = detail;
      }
    }

    titleInputNg() {
      return angular.element(this.titleInput());
    }
    titleInput() {
      if (!this._titleInput) {
        this._titleInput = this.$element[0].querySelector(
          ".cis__mandatory__title"
        );
      }
      return this._titleInput;
    }
  }

  Controller.$inject = serviceDependencies;

  return {
    restrict: "E",
    controller: Controller,
    controllerAs: "self",
    bindToController: true, //scope-bindings -> ctrl
    scope: {
      isOrSeeks: "=",
      /*
             * Usage:
             *  on-update="::myCallback(draft)"
             */
      onUpdate: "&",
    },
    template: template,
  };
}

export default //.controller('CreateNeedController', [...serviceDependencies, CreateNeedController])
angular
  .module("won.owner.components.createIsseek", [])
  .directive("wonCreateIsseeks", genComponentConf).name;
<commit_msg>Added callback method for route picker.<commit_after>/**
 * Created by ksinger on 24.08.2015.
 */
import angular from "angular";

import "ng-redux";
import won from "../won-es6.js";
import { postTitleCharacterLimit } from "config";
import { attach, deepFreeze, clone, dispatchEvent } from "../utils.js";
import { actionCreators } from "../actions/actions.js";
import { connect2Redux } from "../won-utils.js";

const emptyDraft = deepFreeze({
  title: "",
  type: won.WON.BasicNeedTypeCombined,
  description: "",
  tags: [],
  location: undefined,
  fromLocation: undefined,
  toLocation: undefined,
  thumbnail: undefined,
  matchingContext: undefined,
});

// const availableDetails = {
//   description: {},
//   location : {
//     detailName: "location",
//     detailTitle: "Location",
//     detailIcon: "ico36_location_circle",
//     detailComponent: "won-location-picker",
//   },
//   tags: {},
//   ttl: {},
// };

//TODO: can't inject $scope with the angular2-router, preventing redux-cleanup
const serviceDependencies = [
  "$ngRedux",
  "$scope",
  "$element" /*, '$routeParams' /*injections as strings here*/,
];

function genComponentConf() {
  const template = `

            <input
                type="text"
                maxlength="{{self.characterLimit}}"
                class="cis__mandatory__title won-txt"
                placeholder="What? - Short title shown in lists"
                ng-blur="::self.updateTitle()"
                ng-keyup="::self.updateTitle()"/>

                    Add more detail
                    <svg class="cis__addDetail__header__carret" ng-show="!self.showDetail">
                        <use xlink:href="#ico16_arrow_down" href="#ico16_arrow_down"></use>
                    </svg>
                    <svg class="cis__addDetail__header__carret" ng-show="self.showDetail">
                        <use xlink:href="#ico16_arrow_up" href="#ico16_arrow_up"></use>
                    </svg>

                        <svg class="cis__circleicon" ng-show="!self.details.has('description')">
                            <use xlink:href="#ico36_description_circle" href="#ico36_description_circle"></use>
                        </svg>
                        <svg class="cis__circleicon" ng-show="self.details.has('description')">
                            <use xlink:href="#ico36_added_circle" href="#ico36_added_circle"></use>
                        </svg>
                        Description

                        <svg class="cis__circleicon" ng-show="!self.details.has('location')">
                            <use xlink:href="#ico36_location_circle" href="#ico36_location_circle"></use>
                        </svg>
                        <svg class="cis__circleicon" ng-show="self.details.has('location')">
                            <use xlink:href="#ico36_added_circle" href="#ico36_added_circle"></use>
                        </svg>
                        Location

                        <svg class="cis__circleicon" ng-show="!self.details.has('route')">
                            <use xlink:href="#ico36_location_circle" href="#ico36_location_circle"></use>
                        </svg>
                        <svg class="cis__circleicon" ng-show="self.details.has('route')">
                            <use xlink:href="#ico36_added_circle" href="#ico36_added_circle"></use>
                        </svg>
                        Route (From - To)

                        <svg class="cis__circleicon" ng-show="!self.details.has('tags')">
                            <use xlink:href="#ico36_tags_circle" href="#ico36_tags_circle"></use>
                        </svg>
                        <svg class="cis__circleicon" ng-show="self.details.has('tags')">
                            <use xlink:href="#ico36_added_circle" href="#ico36_added_circle"></use>
                        </svg>
                        Tags

                        <svg class="cis__circleicon" ng-show="!self.details.has('ttl')">
                            <use xlink:href="#ico36_rdf_logo_circle" href="#ico36_rdf_logo_circle"></use>
                        </svg>
                        <svg class="cis__circleicon" ng-show="self.details.has('ttl')">
                            <use xlink:href="#ico36_added_circle" href="#ico36_added_circle"></use>
                        </svg>
                        Turtle (TTL)

            <won-description-picker
              ng-if="self.openDetail === 'description'"
              initial-description="::self.draftObject.description"
              on-description-updated="::self.updateDescription(description)">
            </won-description-picker>

            <won-location-picker
                ng-if="self.openDetail === 'location'"
                initial-location="::self.draftObject.location"
                on-location-picked="::self.updateLocation(location)">
            </won-location-picker>

            <won-route-picker
              ng-if="self.openDetail === 'route'"
              initial-fromLocation="::self.draftObject.fromLocation"
              initial-toLocation="::self.draftObject.toLocation"
              on-route-updated="::self.updateRoute(fromLocation, toLocation)">
            </won-route-picker>

            <won-tags-picker
                ng-if="self.openDetail === 'tags'"
                initial-tags="::self.draftObject.tags"
                on-tags-updated="::self.updateTags(tags)">
            </won-tags-picker>

            <won-ttl-picker
              ng-if="self.openDetail === 'ttl'"
              initial-ttl="::self.draftObject.ttl"
              on-ttl-updated="::self.updateTTL(ttl)">
            </won-ttl-picker>

`;

  class Controller {
    constructor(/* arguments <- serviceDependencies */) {
      attach(this, serviceDependencies, arguments);
      this.won = won;

      //TODO: debug; deleteme
      window.cis4dbg = this;

      this.characterLimit = postTitleCharacterLimit;

      this.openDetail = undefined;

      this.reset();

      const selectFromState = () => ({});

      // Using actionCreators like this means that every action defined there is available in the template.
      connect2Redux(selectFromState, actionCreators, [], this);
    }

    reset() {
      this.draftObject = clone(emptyDraft);
      this.details = new Set(); // remove all detail-cards

      this.showDetail = false; // and close selector
    }

    updateDraft() {
      if (!this.details.has("location")) {
        this.draftObject.location = undefined;
      }
      if (!this.details.has("tags")) {
        this.draftObject.tags = [];
      }
      if (!this.details.has("ttl")) {
        this.draftObject.ttl = undefined;
      }
      if (!this.details.has("description")) {
        this.draftObject.description = undefined;
      }
      if (!this.description.has("route")) {
        this.draftObject.fromLocation = undefined;
        this.draftObject.toLocation = undefined;
      }

      this.onUpdate({ draft: this.draftObject });
      dispatchEvent(this.$element[0], "update", { draft: this.draftObject });
    }

    setDraft(updatedDraft) {
      Object.assign(this.draftObject, updatedDraft);
      this.updateDraft();
    }

    updateTitle() {
      const titleString = (this.titleInput() || {}).value || "";
      this.draftObject.title = titleString;
      this.updateDraft();
    }

    updateDescription(description) {
      console.log("description updated with: ", description);
      if (description && description.length > 0) {
        if (!this.details.has("description")) {
          this.details.add("description");
        }
        this.draftObject.description = description;
      } else if (this.details.has("description")) {
        this.details.delete("description");
        this.draftObject.description = undefined;
      }
    }

    updateLocation(location) {
      if (location) {
        if (!this.details.has("location")) {
          this.details.add("location");
        }
        this.draftObject.location = location;
      } else if (this.details.has("location")) {
        this.details.delete("location");
        this.draftObject.location = undefined;
      }

      this.updateDraft();
    }

    updateRoute(fromLocation, toLocation) {
      if (fromLocation || toLocation) {
        if (!this.details.has("route")) {
          this.details.add("route");
        }
        this.draftObject.fromLocation = fromLocation;
        this.draftObject.toLocation = toLocation;
      } else if (this.details.has("route")) {
        this.details.delete("route");
        this.draftObject.fromLocation = undefined;
        this.draftObject.toLocation = undefined;
      }

      this.updateDraft();
    }

    updateTags(tags) {
      if (tags && tags.length > 0) {
        if (!this.details.has("tags")) {
          this.details.add("tags");
        }
        this.draftObject.tags = tags;
      } else if (this.details.has("tags")) {
        this.details.delete("tags");
        this.draftObject.tags = [];
      }

      this.updateDraft();
    }

    updateTTL(ttl) {
      if (ttl && ttl.length > 0) {
        if (!this.details.has("ttl")) {
          this.details.add("ttl");
        }
        this.draftObject.ttl = ttl;
      } else if (this.details.has("ttl")) {
        this.details.delete("ttl");
        this.draftObject.ttl = undefined;
      }

      this.updateDraft();
    }

    pickImage(image) {
      this.draftObject.thumbnail = image;
    }

    toggleDetail() {
      this.showDetail = !this.showDetail;
    }

    toggleOpenDetail(detail) {
      // open clicked detail
      if (this.openDetail === detail) {
        this.openDetail = undefined;
      } else {
        this.openDetail = detail;
      }
    }

    titleInputNg() {
      return angular.element(this.titleInput());
    }
    titleInput() {
      if (!this._titleInput) {
        this._titleInput = this.$element[0].querySelector(
          ".cis__mandatory__title"
        );
      }
      return this._titleInput;
    }
  }

  Controller.$inject = serviceDependencies;

  return {
    restrict: "E",
    controller: Controller,
    controllerAs: "self",
    bindToController: true, //scope-bindings -> ctrl
    scope: {
      isOrSeeks: "=",
      /*
             * Usage:
             *  on-update="::myCallback(draft)"
             */
      onUpdate: "&",
    },
    template: template,
  };
}

export default //.controller('CreateNeedController', [...serviceDependencies, CreateNeedController])
angular
  .module("won.owner.components.createIsseek", [])
  .directive("wonCreateIsseeks", genComponentConf).name;
<|endoftext|>


<commit_before>/**
 * Created by ksinger on 17.06.2016.
 */

;

// import Medium from '../mediumjs-es6';
import angular from 'angular';
import 'ng-redux';
import Immutable from 'immutable';
import 'angular-sanitize';
import { dispatchEvent, attach, delay } from '../utils';
import { actionCreators }  from '../actions/actions';

function genComponentConf() {
    let template = `

    `;

    const serviceDependencies = ['$scope', '$element', '$ngRedux', /*injections as strings here*/];

    class Controller {
        constructor(/* arguments <- serviceDependencies */) {
            attach(this, serviceDependencies, arguments);
            window.ntf4dbg = this;

            //this.characterLimit = 140; //COMMENT BECAUSE WE DO NOT NEED IT FOR NOW TODO move to conf

            const selectFromState = (state) => ({
                draftId: state.getIn(['router', 'currentParams', 'draftId'])
            })
            const disconnect = this.$ngRedux.connect(selectFromState, actionCreators)(this);
            this.$scope.$on('$destroy', disconnect);

            this.initMedium();

            this.mediumMountNg().bind('input', e => this.input(e));
            this.mediumMountNg().bind('paste', e => this.input(e));
        }
        /*charactersLeft() {
            return this.characterLimit - this.medium.value().length;
        }*/
        input(e) {
            var description;
            var title;
            var tags;

            const paragraphsDom = this.$element.find('p').toArray();
            const paragraphsNg = paragraphsDom.map(p => angular.element(p)); // how performant is `.element`?
            paragraphsNg.map(p => p.removeClass("medium_title"));

            if(paragraphsDom && paragraphsDom.length > 1){
                const titleParagraphDom = paragraphsDom[0];
                const titleParagraphNg = paragraphsNg[0];
                titleParagraphNg.addClass("medium_title");

                /*
                 * Remove placeholder-white-space if medium.js fails to remove it,
                 * e.g. when pasting (a multi-line'd string) into an empty textfield.
                 * The `[0]` access the dom-element inside of the angular-element.
                 */
                titleParagraphDom.innerHTML = titleParagraphDom.innerHTML.replace(/^ /, '');

                title = titleParagraphNg.text();

                const bodyParagraphs = angular.element(".medium-mount p:not('.medium_title')");
                if(bodyParagraphs && bodyParagraphs.length > 0){
                    description = "";
                    bodyParagraphs.each(function(){
                        description += angular.element(this).text() +"\n";
                    });
                }
            } else {
                title  = angular.element(".medium-mount p:first").text();
                description = undefined;
            }

            //ADD TAGS
            var titleTags = title? title.match(/#(\w+)/gi) : [];
            var descriptionTags = description? description.match(/#(\w+)/gi) : [];

            tags = angular.element.unique(
                angular.element.merge(
                    titleTags ? titleTags : [],
                    descriptionTags ? descriptionTags : []
                )
            );

            for(var i=0; i<tags.length; i++){
                tags[i] = tags[i].substr(1);
            }

            //SAVE TO STATE
            this.drafts__change__description({
                draftId: this.draftId,
                description : description
            });

            this.drafts__change__title({
                draftId: this.draftId,
                title: title.replace(" ","").trim() //TODO: MOVE THIS HACK TO VIEW LEVEL
            });

            this.drafts__change__tags({
                draftId: this.draftId,
                tags: tags && tags.length > 0? tags : undefined
            });
        }
        valid() {
            return true; //return this.charactersLeft() >= 0;
        }

        value() {
            return this.medium
                .value()
                /*
                 * the replace fixes odd behaviour of FF. it inserts
                 * a `<br>` at the end after the first space is
                 * typed -- unless the space is the first character
                 * in the field.
                 */
                .replace(/
$/, '');
        }

        initMedium() {
            // initialising editor. see http://jakiestfu.github.io/Medium.js/docs/
            this.medium = new Medium({
                element: this.mediumMount(),

                modifier: 'auto',
                placeholder: 'What',
                autoHR: false, //if true, inserts
 after two empty lines
                //mode: Medium.inlineMode, // no newlines, no styling
                mode: Medium.partialMode, // allows newlines, no styling
                //maxLength: this.maxChars, // -1 would disable it

                //tags: null,
                /*
                tags: {
                    'break': 'br',
                    // 'horizontalRule': 'hr',
                    'paragraph': 'p',
                    // 'outerLevel': ['pre', 'blockquote', 'figure'],
                    // 'innerLevel': ['a', 'b', 'u', 'i', 'img', 'strong']
                    //'outerLevel': [], //should disable all tags
                    //'innerLevel': [], //should disable all tags
                },
                */
                attributes: {
                    //remove: ['style', 'class'] //TODO does this remove the ng-class?
                    remove: ['style'] //TODO does this remove the ng-class?
                },
                //pasteAsText: false,
                //pasteAsText: true,
                beforeInsertHtml: function () {
                    //this = Medium.Html (!)

                    // Replace `<br>`s with the `<p>`s we use for line-breaking, to allow
                    // multi-line pasting. This assumes that pasting happens inside
                    // a `<p>` element and will horribly fail otherwise.
                    // The trimming happens, as leading whitespaces are removed in other
                    // lines as well during pasting.
                    const originalHtml = this.html;
                    const sanitizedHtml = originalHtml.trim().replace(/
/g, '

');
                    this.html = sanitizedHtml;

                    console.log('medium.js - beforeInsertHtml: ', this, originalHtml, sanitizedHtml);

                    //this.html = 'trololololo';
                    // TODO: parse
                    //
 ->
 (also add opening tag!)
                    //

 ->
 +

                },
                /*
                */
                /*
                pasteEventHandler: function(e) {
                    //default paste event handler
                    //enables paste (dunno why) but also breaks the inlineMode (suddenly there's
 elements)
                    console.log('medium.js - pasteEventHandler: ', this, arguments)
                }
                */

                /*
                TODO stopped here

                 pasteAsText?
                 intercept paste and replace
s with
s
                 beforeInserHtml
                 beforeAddTag - intercept any

                 intercept all paste events and call insertHtml manually?

                */
                /*
                beforeInvokeElement: function () {
                 //this = Medium.Element
                     console.log('beforeInvokeElement: ', this)
                 },
                 beforeAddTag: function (tag, shouldFocus, isEditable, afterElement) {
                     console.log('beforeAddTag: ', this, arguments)
                 },
                 keyContext: null, //what does this do?
                */

            });

            //remove the inline-styles placed by medium.js
            this.medium.placeholder.style = "";
            this.mediumMount().addEventListener('blur', e =>
                delay(0) //push to end end of task-queue (and thus all other `blur`-listeners
                    .then(() => {
                        const style = this.medium.placeholder.style;
                        style.minHeight = 0;
                        style.minWidth = 0;
                    })
            );
        }

        mediumMountNg() {
            if(!this._mediumMount) {
                this._mediumMount = this.textFieldNg().find('.medium-mount')
            }
            return this._mediumMount;
        }

        mediumMount() {
            return this.mediumMountNg()[0];
        }

        textFieldNg() {
            if(!this._textField) {
                this._textField = this.$element.find('.wdt__text');
            }
            return this._textField;
        }
        textField() {
            return this.textFieldNg()[0];
        }
    }
    Controller.$inject = serviceDependencies;

    return {
        restrict: 'E',
        controller: Controller,
        controllerAs: 'self',
        bindToController: true, //scope-bindings -> ctrl
        scope: { },
        template: template
    }
}

export default angular.module('won.owner.components.needTextfield', [ ])
    .directive('needTextfield', genComponentConf)
    .name;<commit_msg>Refactored need-description parsing a bit to streamline the process and deal with contenteditable quirks.<commit_after>/**
 * Created by ksinger on 17.06.2016.
 */

;

// import Medium from '../mediumjs-es6';
import angular from 'angular';
import 'ng-redux';
import Immutable from 'immutable';
import 'angular-sanitize';
import { dispatchEvent, attach, delay } from '../utils';
import { actionCreators }  from '../actions/actions';

function genComponentConf() {
    let template = `

    `;

    const serviceDependencies = ['$scope', '$element', '$ngRedux', /*injections as strings here*/];

    class Controller {
        constructor(/* arguments <- serviceDependencies */) {
            attach(this, serviceDependencies, arguments);
            window.ntf4dbg = this;

            //this.characterLimit = 140; //COMMENT BECAUSE WE DO NOT NEED IT FOR NOW TODO move to conf

            const selectFromState = (state) => ({
                draftId: state.getIn(['router', 'currentParams', 'draftId'])
            })
            const disconnect = this.$ngRedux.connect(selectFromState, actionCreators)(this);
            this.$scope.$on('$destroy', disconnect);

            this.initMedium();

            this.mediumMountNg().bind('input', e => this.input(e));
            this.mediumMountNg().bind('paste', e => this.input(e));
        }
        /*charactersLeft() {
            return this.characterLimit - this.medium.value().length;
        }*/
        input(e) {

            const paragraphsDom = this.$element.find('p').toArray();
            const paragraphsNg = paragraphsDom.map(p => angular.element(p)); // how performant is `.element`?
            paragraphsNg.map(p => p.removeClass("medium_title"));

            const titleParagraphDom = paragraphsDom[0];
            const titleParagraphNg = paragraphsNg[0];
            titleParagraphNg.addClass("medium_title");

            var description;
            var tags;
            if(paragraphsDom && paragraphsDom.length > 1){
                const descriptionParagraphs = paragraphsNg.slice(1);
                description = descriptionParagraphs.map(p =>
                        p.text()
                          /* remove trailing white-spaces (e.g. bogus-line-breaks,
                           * i.e. the ones that aren't
)
                           */
                          .replace(/\s*$/,'')
                    )
                    .join('\n') // concatenate lines
                    /*
                     * remove leading/trailing empty lines that occur between title
                     * and description when pasting multi-line text
                    */
                    .trim();
            } else {
                description = undefined;
            }

            /*
             * Remove placeholder-white-space if medium.js fails to remove it,
             * e.g. when pasting (a multi-line'd string) into an empty textfield.
             * The `[0]` access the dom-element inside of the angular-element.
             */
            titleParagraphDom.innerHTML = titleParagraphDom.innerHTML.replace(/^ /, '');

            const title = titleParagraphDom.innerHTML
                //@`replace`: sometimes mediumjs doesn't remove the placeholder nbsp properly.
                //.replace(/^ /, '')
                .trim();

            //ADD TAGS
            const titleTags = title? title.match(/#(\w+)/gi) : [];
            const descriptionTags = description? description.match(/#(\w+)/gi) : [];

            tags = angular.element.unique(
                angular.element.merge(
                    titleTags ? titleTags : [],
                    descriptionTags ? descriptionTags : []
                )
            );

            for(var i=0; i<tags.length; i++){
                tags[i] = tags[i].substr(1);
            }

            //SAVE TO STATE //TODO: MOVE THIS HACK TO VIEW LEVEL (to make component reusable)
            this.drafts__change__description({
                draftId: this.draftId,
                description : description,
            });

            this.drafts__change__title({
                draftId: this.draftId,
                title: title,
            });

            this.drafts__change__tags({
                draftId: this.draftId,
                tags: tags && tags.length > 0? tags : undefined
            });
        }
        valid() {
            return true; //return this.charactersLeft() >= 0;
        }

        value() {
            return this.medium
                .value()
                /*
                 * the replace fixes odd behaviour of FF. it inserts
                 * a `<br>` at the end after the first space is
                 * typed -- unless the space is the first character
                 * in the field.
                 */
                .replace(/
$/, '');
        }

        initMedium() {
            // initialising editor. see http://jakiestfu.github.io/Medium.js/docs/
            this.medium = new Medium({
                element: this.mediumMount(),

                modifier: 'auto',
                placeholder: 'What',
                autoHR: false, //if true, inserts
 after two empty lines
                //mode: Medium.inlineMode, // no newlines, no styling
                mode: Medium.partialMode, // allows newlines, no styling
                //maxLength: this.maxChars, // -1 would disable it

                //tags: null,
                /*
                tags: {
                    'break': 'br',
                    // 'horizontalRule': 'hr',
                    'paragraph': 'p',
                    // 'outerLevel': ['pre', 'blockquote', 'figure'],
                    // 'innerLevel': ['a', 'b', 'u', 'i', 'img', 'strong']
                    //'outerLevel': [], //should disable all tags
                    //'innerLevel': [], //should disable all tags
                },
                */
                attributes: {
                    //remove: ['style', 'class'] //TODO does this remove the ng-class?
                    remove: ['style'] //TODO does this remove the ng-class?
                },
                //pasteAsText: false,
                //pasteAsText: true,
                beforeInsertHtml: function () {
                    //this = Medium.Html (!)

                    // Replace `<br>`s with the `<p>`s we use for line-breaking, to allow
                    // multi-line pasting. This assumes that pasting happens inside
                    // a `<p>` element and will horribly fail otherwise.
                    // The trimming happens, as leading whitespaces are removed in other
                    // lines as well during pasting.
                    const originalHtml = this.html;
                    const sanitizedHtml = originalHtml.trim().replace(/
/g, '

');
                    this.html = sanitizedHtml;

                    console.log('medium.js - beforeInsertHtml: ', this, originalHtml, sanitizedHtml);

                    //this.html = 'trololololo';
                    // TODO: parse
                    //
 ->
 (also add opening tag!)
                    //

 ->
 +

                },
                /*
                */
                /*
                pasteEventHandler: function(e) {
                    //default paste event handler
                    //enables paste (dunno why) but also breaks the inlineMode (suddenly there's
 elements)
                    console.log('medium.js - pasteEventHandler: ', this, arguments)
                }
                */

                /*
                TODO stopped here

                 pasteAsText?
                 intercept paste and replace
s with
s
                 beforeInserHtml
                 beforeAddTag - intercept any

                 intercept all paste events and call insertHtml manually?

                */
                /*
                beforeInvokeElement: function () {
                 //this = Medium.Element
                     console.log('beforeInvokeElement: ', this)
                 },
                 beforeAddTag: function (tag, shouldFocus, isEditable, afterElement) {
                     console.log('beforeAddTag: ', this, arguments)
                 },
                 keyContext: null, //what does this do?
                */

            });

            //remove the inline-styles placed by medium.js
            this.medium.placeholder.style = "";
            this.mediumMount().addEventListener('blur', e =>
                delay(0) //push to end end of task-queue (and thus all other `blur`-listeners
                    .then(() => {
                        const style = this.medium.placeholder.style;
                        style.minHeight = 0;
                        style.minWidth = 0;
                    })
            );
        }

        mediumMountNg() {
            if(!this._mediumMount) {
                this._mediumMount = this.textFieldNg().find('.medium-mount')
            }
            return this._mediumMount;
        }

        mediumMount() {
            return this.mediumMountNg()[0];
        }

        textFieldNg() {
            if(!this._textField) {
                this._textField = this.$element.find('.wdt__text');
            }
            return this._textField;
        }
        textField() {
            return this.textFieldNg()[0];
        }
    }
    Controller.$inject = serviceDependencies;

    return {
        restrict: 'E',
        controller: Controller,
        controllerAs: 'self',
        bindToController: true, //scope-bindings -> ctrl
        scope: { },
        template: template
    }
}

export default angular.module('won.owner.components.needTextfield', [ ])
    .directive('needTextfield', genComponentConf)
    .name;<|endoftext|>