<commit_before>/**
 * Tavuttajan tavoite on helppokäyttöinen ja kevyt selainpuolen
 * tekstien tavutus. Scriptin tarkoitus ei ole olla täydellinen
 * tavuttaja, vaan tavuttaa varmimmat tapaukset. Mielummin
 * tavuttaja jättää tavuttamatta kuin tavuttaa epävarman paikan.
 *
 * Hyphenation rules:
 *
 * [1] Tavuraja on aina konsonantin ja vokaalin yhdistelmän edellä
 * http://www.kotus.fi/index.phtml?s=4363, Hakupäivä 10.6.2014
 *
 * Author: Veikko Karsikko
 */

;(function($, window, document, undefined) {
	var vowels = "aeiouyåäö";
	var consonants = "bcdfghjklmnpqrstvwxyz";
	var hyphMark = "";
	// construct regExps only once
	var rule1RegExp = new RegExp("[" + consonants + "][" + vowels + "]", "gi");
	var endReg = new RegExp("[" + consonants + vowels + "]{2}$", "i");

	var pluginName = "finnishHyphenator";

	var addHyphen = function(text, idx) {
		var firstPart = text.slice(0, idx);
		if (endReg.exec(firstPart)) {
			return (text.slice(0, idx) + hyphMark + text.slice(idx));
		} else {
			return false;
		}
	};

	var $wrapperElement = $("

");
	var hyphenateElement = function($el) {
		$el.contents().each(function() {
			var $node = $(this);
			if (this.nodeType === 3) {
				// is text element and will be hyphenated
				var hyphenated = hyphenateText(this.textContent);
				$wrapperElement.html(hyphenated);
				$node.before($wrapperElement.html());
				$node.remove();
			} else {
				hyphenateElement($node);
			}
		});
	};

	var hyphenateText = function(text) {
		var hyphenated = text;
		var hyphPositions = [];

		// rule [1]
		while ((match = rule1RegExp.exec(text)) != null) {
			hyphPositions.push(match.index);
		}

		var count = 0;
		for (var i = 0; i < hyphPositions.length; i++) {
			var position = hyphPositions[i] + count * hyphMark.length;
			var triedHyphenation = addHyphen(hyphenated, position);
			if (triedHyphenation !== false) {
				count++;
				hyphenated = triedHyphenation;
			}
		}
		return hyphenated;
	};

	function Plugin(element) {
		this.element = element;
		this._name = pluginName;
		this.init();
	}

	$.extend(Plugin.prototype, {
		init: function() {
			hyphenateElement($(this.element));
		}
	});

	$.fn[pluginName] = function(options) {
		this.each(function() {
			if (!$.data(this, "plugin_" + pluginName)) {
				$.data(this, "plugin_" + pluginName, new Plugin(this, options));
			}
		});
		return this;
	};

})(jQuery, window, document);<commit_msg>Whitespace cleanup<commit_after>/**
 * Tavuttajan tavoite on helppokäyttöinen ja kevyt selainpuolen
 * tekstien tavutus. Scriptin tarkoitus ei ole olla täydellinen
 * tavuttaja, vaan tavuttaa varmimmat tapaukset. Mielummin
 * tavuttaja jättää tavuttamatta kuin tavuttaa epävarman paikan.
 *
 * Hyphenation rules:
 *
 * [1] Tavuraja on aina konsonantin ja vokaalin yhdistelmän edellä
 * http://www.kotus.fi/index.phtml?s=4363, Hakupäivä 10.6.2014
 *
 * Author: Veikko Karsikko
 */

;(function($, window, document, undefined) {
	var vowels = "aeiouyåäö";
	var consonants = "bcdfghjklmnpqrstvwxyz";
	var hyphMark = "";
	// construct regExps only once
	var rule1RegExp = new RegExp("[" + consonants + "][" + vowels + "]", "gi");
	var endReg = new RegExp("[" + consonants + vowels + "]{2}$", "i");

	var pluginName = "finnishHyphenator";

	var addHyphen = function(text, idx) {
		var firstPart = text.slice(0, idx);
		if (endReg.exec(firstPart)) {
			return (text.slice(0, idx) + hyphMark + text.slice(idx));
		} else {
			return false;
		}
	};

	var $wrapperElement = $("

");
	var hyphenateElement = function($el) {
		$el.contents().each(function() {
			var $node = $(this);
			if (this.nodeType === 3) {
				// is text element and will be hyphenated
				var hyphenated = hyphenateText(this.textContent);
				$wrapperElement.html(hyphenated);
				$node.before($wrapperElement.html());
				$node.remove();
			} else {
				hyphenateElement($node);
			}
		});
	};

	var hyphenateText = function(text) {
		var hyphenated = text;
		var hyphPositions = [];

		// rule [1]
		while ((match = rule1RegExp.exec(text)) != null) {
			hyphPositions.push(match.index);
		}

		var count = 0;
		for (var i = 0; i < hyphPositions.length; i++) {
			var position = hyphPositions[i] + count * hyphMark.length;
			var triedHyphenation = addHyphen(hyphenated, position);
			if (triedHyphenation !== false) {
				count++;
				hyphenated = triedHyphenation;
			}
		}
		return hyphenated;
	};

	function Plugin(element) {
		this.element = element;
		this._name = pluginName;
		this.init();
	}

	$.extend(Plugin.prototype, {
		init: function() {
			hyphenateElement($(this.element));
		}
	});

	$.fn[pluginName] = function(options) {
		this.each(function() {
			if (!$.data(this, "plugin_" + pluginName)) {
				$.data(this, "plugin_" + pluginName, new Plugin(this, options));
			}
		});
		return this;
	};

})(jQuery, window, document);
<|endoftext|>


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

	let isCreate, exercise, hasPattern;

	function getDefaultExercise() {
		return {
			type: 3,
			names: [],
			descriptions: [],
			solution: [null],
			solutionVisible: false
		};
	}

	function testRegExp(pattern) {
		try {
			return !pattern || new RegExp(pattern);
		} catch (ex) {
			return false;
		}
	}

	function testSolution(pattern, solution) {
		return solution && solution.length && new RegExp(pattern).test(solution);
	}

	function testValidExercise({ programmingLanguage, category_id, difficulty, names, descriptions, pattern, solution }) {
		const notEmpty = /[^\s]+/;
		return programmingLanguage && _.some(Progressor.getProgrammingLanguages(), l => l._id === programmingLanguage)
					 && category_id && Progressor.categories.find({ _id: category_id }).count() === 1
					 && difficulty && _.contains(Progressor.getDifficulties(), difficulty)
					 && names && names.length && _.some(names, n => n.name && notEmpty.test(n.name))
					 && descriptions && descriptions.length && _.some(descriptions, d => d.description && notEmpty.test(d.description))
					 && testRegExp(pattern)
					 && _.every(solution, s => notEmpty.test(s) && testSolution(pattern, s));
	}

	Template.textEdit.onRendered(function () {
		this.autorun(function () {
			const live = Progressor.exercises.findOne();
			const detached = Tracker.nonreactive(() => exercise.get());
			if (!live || !detached || live._id !== detached._id) {
				let _exercise = live || getDefaultExercise();
				if (isCreate.get())
					_exercise = _.omit(_exercise, '_id', 'released', 'author_id', 'lastEditor_id', 'lastEdited');
				exercise.set(Progressor.joinCategory(_exercise));
			} else if (live.lastEditor_id !== Meteor.userId())
				Progressor.showAlert(i18n('form.documentChangedMessage'));
		});
	});

	Template.textEdit.onCreated(function () {
		isCreate = new ReactiveVar(false);
		exercise = new ReactiveVar(getDefaultExercise());
	});

	Template.textEdit.helpers(
		{
			safeExercise(context) {
				isCreate.set(!context || !context._id);
				return exercise.get();
			},
			exists: () => exercise.get() && exercise.get()._id,
			canSave: () => !exercise.get() || !exercise.get()._id || !exercise.get().released || !exercise.get().released.requested || Roles.userIsInRole(Meteor.userId(), Progressor.ROLE_ADMIN),
			exerciseSearchData: () => ({ _id: exercise.get().programmingLanguage }),
			exerciseDuplicateQuery: () => ({ duplicate: exercise.get()._id }),
			categoryEditData: () => ( exercise.get() && exercise.get().category_id ? { _id: exercise.get().category_id } : null),
			userName: Progressor.getUserName,
			not: b => !b,
			i18nProgrammingLanguages: () => _.map(Progressor.getProgrammingLanguages(), language => _.extend({}, language, {
				name: i18n.getProgrammingLanguage(language._id),
				isActive: language._id === exercise.get().programmingLanguage
			})),
			i18nCategories: () => Progressor.categories.find({ programmingLanguage: exercise.get().programmingLanguage }).map(category => _.extend({}, category, {
				name: i18n.getName(category),
				isActive: category._id === exercise.get().category_id
			})),
			i18nDifficulties: () => _.map(Progressor.getDifficulties(), difficulty => ({
				_id: difficulty, name: i18n.getDifficulty(difficulty),
				isActive: difficulty === exercise.get().difficulty
			})),
			i18nExerciseNamesDescriptions: () => _.map(i18n.getLanguages(), (name, id) => ({
				_id: id, language: name, isActive: id === i18n.getLanguage(),
				name: i18n.getNameForLanguage(exercise.get(), id),
				description: i18n.getDescriptionForLanguage(exercise.get(), id)
			})),
			hasPattern: () => hasPattern
		});

	function changeExercise(cb) {
		return function (ev) {
			const ret = cb(ev, ev && ev.currentTarget ? $(ev.currentTarget) : null);
			exercise.dep.changed();
			return ret;
		};
	}

	function changeExerciseCollection(collectionName, cssClass, propertiesFunction) {
		return changeExercise((ev, $this) => _.extend(exercise.get()[collectionName][$this.closest(`.${cssClass}`).prevAll(`.${cssClass}`).length], propertiesFunction(ev, $this)));
	}

	function changeExerciseTranslation(translationName) {
		return changeExercise(function (ev, $this) {
			const value = $this.val(), elements = exercise.get()[`${translationName}s`], language = $this.closest('[data-lang]').data('lang');
			let elementIndex = -1;
			const element = _.find(elements, (e, i) => (elementIndex = e.language === language ? i : elementIndex) >= 0);
			if (!value) elements.splice(elementIndex, 1);
			else if (element) element[translationName] = value;
			else elements.push({ language, [translationName]: value });
		});
	}

	function addExerciseCollectionItem(collectionName, cssClass, generator) {
		return changeExercise(function (ev, $this) {
			const removeIndex = $this.closest(`.${cssClass}`).prevAll(`.${cssClass}`).length;
			exercise.get()[collectionName].splice(removeIndex + 1, 0, generator(ev, $this));
		});
	}

	function removeExerciseCollectionItem(collectionName, cssClass) {
		return changeExercise(function (ev, $this) {
			const removeIndex = $this.closest(`.${cssClass}`).prevAll(`.${cssClass}`).length;
			exercise.get()[collectionName].splice(removeIndex, 1);
		});
	}

	Template.textEdit.events(
		{
			'click .btn-add-solution': addExerciseCollectionItem('solution', 'container-solution', () => getDefaultExercise().solution[0]),
			'click .btn-remove-solution': removeExerciseCollectionItem('solution'),
			'keyup #input-pattern'(ev) {
				const $this = $(ev.currentTarget), $group = $this.closest('.form-group').removeClass('has-error'), pattern = $this.val();
				if (!testRegExp(pattern))
					$group.addClass('has-error');
			},
			'keyup #textarea-solution'(ev) {
				const $this = $(ev.currentTarget), $group = $this.closest('.form-group').removeClass('has-error'), pattern = $('#input-pattern').val();
				if (testRegExp(pattern) && !testSolution(pattern, $this.val()))
					$group.addClass('has-error');
			},
			'change #select-language': changeExercise((ev, $this) => !exercise.get()._id ? exercise.get().programmingLanguage = $this.val() : null),
			'change #select-category': changeExercise((ev, $this) => exercise.get().category_id = $this.val()),
			'change #select-difficulty': changeExercise((ev, $this) => exercise.get().difficulty = parseInt($this.val())),
			'change [id^="input-name-"]': changeExerciseTranslation('name'),
			'change [id^="textarea-description-"]': changeExerciseTranslation('description'),
			'change #input-pattern': changeExercise((ev, $this) => $this.val().length ? exercise.get().pattern = $this.val() : delete exercise.get().pattern),
			'change #textarea-solution': changeExercise((ev, $this) => exercise.get().solution = [$this.val()]),
			'change .input-solution-text': changeExerciseCollection('solution', 'container-solution', (ev, $this) => $this.val()),
			'change #checkbox-solution-visible': changeExercise((ev, $this) => exercise.get().solutionVisible = $this.prop('checked')),
			'click .btn-save, click .btn-release-request': changeExercise(function (ev, $this) {
				if ($this.hasClass('btn-release-request'))
					exercise.get().released = { requested: new Date() };
				if (testValidExercise(exercise.get()))
					Meteor.call('saveExercise', _.omit(exercise.get(), 'category'), Progressor.handleError(res => Router.go('exerciseSolve', { _id: res }), false));
				else
					Progressor.showAlert(i18n('exercise.isNotValidMessage'));
			}),
			'click .btn-delete': () => Meteor.call('deleteExercise', { _id: exercise.get()._id }, Progressor.handleError(() => Router.go('exerciseSearch', { _id: exercise.get().programmingLanguage }), false))
		});

})();
<commit_msg>free text (issue)<commit_after>(function () {
	'use strict';

	let isCreate, exercise, hasPattern;

	function getDefaultExercise() {
		return {
			type: 3,
			names: [],
			descriptions: [],
			solution: [null],
			solutionVisible: false
		};
	}

	function testRegExp(pattern) {
		try {
			return !pattern || new RegExp(pattern);
		} catch (ex) {
			return false;
		}
	}

	function testSolution(pattern, solution) {
		return solution && solution.length && new RegExp(pattern).test(solution);
	}

	function testValidExercise({ programmingLanguage, category_id, difficulty, names, descriptions, pattern, solution }) {
		const notEmpty = /[^\s]+/;
		return programmingLanguage && _.some(Progressor.getProgrammingLanguages(), l => l._id === programmingLanguage)
					 && category_id && Progressor.categories.find({ _id: category_id }).count() === 1
					 && difficulty && _.contains(Progressor.getDifficulties(), difficulty)
					 && names && names.length && _.some(names, n => n.name && notEmpty.test(n.name))
					 && descriptions && descriptions.length && _.some(descriptions, d => d.description && notEmpty.test(d.description))
					 && testRegExp(pattern)
					 && _.every(solution, s => notEmpty.test(s) && testSolution(pattern, s));
	}

	Template.textEdit.onRendered(function () {
		this.autorun(function () {
			const live = Progressor.exercises.findOne();
			const detached = Tracker.nonreactive(() => exercise.get());
			if (!live || !detached || live._id !== detached._id) {
				let _exercise = live || getDefaultExercise();
				if (isCreate.get())
					_exercise = _.omit(_exercise, '_id', 'released', 'author_id', 'lastEditor_id', 'lastEdited');
				exercise.set(Progressor.joinCategory(_exercise));
			} else if (live.lastEditor_id !== Meteor.userId())
				Progressor.showAlert(i18n('form.documentChangedMessage'));
		});
	});

	Template.textEdit.onCreated(function () {
		isCreate = new ReactiveVar(false);
		exercise = new ReactiveVar(getDefaultExercise());
	});

	Template.textEdit.helpers(
		{
			safeExercise(context) {
				isCreate.set(!context || !context._id);
				return exercise.get();
			},
			exists: () => exercise.get() && exercise.get()._id,
			canSave: () => !exercise.get() || !exercise.get()._id || !exercise.get().released || !exercise.get().released.requested || Roles.userIsInRole(Meteor.userId(), Progressor.ROLE_ADMIN),
			exerciseSearchData: () => ({ _id: exercise.get().programmingLanguage }),
			exerciseDuplicateQuery: () => ({ duplicate: exercise.get()._id }),
			categoryEditData: () => ( exercise.get() && exercise.get().category_id ? { _id: exercise.get().category_id } : null),
			userName: Progressor.getUserName,
			not: b => !b,
			i18nProgrammingLanguages: () => _.map(Progressor.getProgrammingLanguages(), language => _.extend({}, language, {
				name: i18n.getProgrammingLanguage(language._id),
				isActive: language._id === exercise.get().programmingLanguage
			})),
			i18nCategories: () => Progressor.categories.find({ programmingLanguage: exercise.get().programmingLanguage }).map(category => _.extend({}, category, {
				name: i18n.getName(category),
				isActive: category._id === exercise.get().category_id
			})),
			i18nDifficulties: () => _.map(Progressor.getDifficulties(), difficulty => ({
				_id: difficulty, name: i18n.getDifficulty(difficulty),
				isActive: difficulty === exercise.get().difficulty
			})),
			i18nExerciseNamesDescriptions: () => _.map(i18n.getLanguages(), (name, id) => ({
				_id: id, language: name, isActive: id === i18n.getLanguage(),
				name: i18n.getNameForLanguage(exercise.get(), id),
				description: i18n.getDescriptionForLanguage(exercise.get(), id)
			})),
			hasPattern: () => hasPattern
		});

	function changeExercise(cb) {
		return function (ev) {
			const ret = cb(ev, ev && ev.currentTarget ? $(ev.currentTarget) : null);
			exercise.dep.changed();
			return ret;
		};
	}

	function changeExerciseCollection(collectionName, cssClass, propertiesFunction) {
		return changeExercise((ev, $this) => _.extend(exercise.get()[collectionName][$this.closest(`.${cssClass}`).prevAll(`.${cssClass}`).length], propertiesFunction(ev, $this)));
	}

	function changeExerciseTranslation(translationName) {
		return changeExercise(function (ev, $this) {
			const value = $this.val(), elements = exercise.get()[`${translationName}s`], language = $this.closest('[data-lang]').data('lang');
			let elementIndex = -1;
			const element = _.find(elements, (e, i) => (elementIndex = e.language === language ? i : elementIndex) >= 0);
			if (!value) elements.splice(elementIndex, 1);
			else if (element) element[translationName] = value;
			else elements.push({ language, [translationName]: value });
		});
	}

	function addExerciseCollectionItem(collectionName, cssClass, generator) {
		return changeExercise(function (ev, $this) {
			const removeIndex = $this.closest(`.${cssClass}`).prevAll(`.${cssClass}`).length;
			exercise.get()[collectionName].splice(removeIndex + 1, 0, generator(ev, $this));
		});
	}

	function removeExerciseCollectionItem(collectionName, cssClass) {
		return changeExercise(function (ev, $this) {
			const removeIndex = $this.closest(`.${cssClass}`).prevAll(`.${cssClass}`).length;
			exercise.get()[collectionName].splice(removeIndex, 1);
		});
	}

	Template.textEdit.events(
		{
			'click .btn-add-solution': addExerciseCollectionItem('solution', 'container-solution', () => getDefaultExercise().solution[0]),
			'click .btn-remove-solution': removeExerciseCollectionItem('solution'),
			'keyup #input-pattern'(ev) {
				const $this = $(ev.currentTarget), $group = $this.closest('.form-group').removeClass('has-error'), pattern = $this.val();
				if (!testRegExp(pattern))
					$group.addClass('has-error');
			},
			'keyup #textarea-solution'(ev) {
				const $this = $(ev.currentTarget), $group = $this.closest('.form-group').removeClass('has-error'), pattern = $('#input-pattern').val();
				if (testRegExp(pattern) && !testSolution(pattern, $this.val()))
					$group.addClass('has-error');
			},
			'change #select-language': changeExercise((ev, $this) => !exercise.get()._id ? exercise.get().programmingLanguage = $this.val() : null),
			'change #select-category': changeExercise((ev, $this) => exercise.get().category_id = $this.val()),
			'change #select-difficulty': changeExercise((ev, $this) => exercise.get().difficulty = parseInt($this.val())),
			'change [id^="input-name-"]': changeExerciseTranslation('name'),
			'change [id^="textarea-description-"]': changeExerciseTranslation('description'),
			'change #input-pattern': changeExercise((ev, $this) => $this.val().length ? exercise.get().pattern = $this.val() : delete exercise.get().pattern),
			'change #textarea-solution': changeExercise((ev, $this) => exercise.get().solution = [$this.val()]),
			'change .input-solution-text': changeExerciseCollection('solution', 'container-solution', (ev, $this) => $this.val()),
			'change #checkbox-solution-visible': changeExercise((ev, $this) => exercise.get().solutionVisible = $this.prop('checked')),
			'click .btn-save, click .btn-release-request': changeExercise(function (ev, $this) {
				if ($this.hasClass('btn-release-request'))
					exercise.get().released = { requested: new Date() };
				if (testValidExercise(exercise.get()))
					Meteor.call('saveExercise', _.omit(exercise.get(), 'category'), Progressor.handleError(res => Router.go('exerciseSolve', { _id: res }), false));
				else
					Progressor.showAlert(i18n('exercise.isNotValidMessage'));
			}),
			'click .btn-delete': () => Meteor.call('deleteExercise', { _id: exercise.get()._id }, Progressor.handleError(() => Router.go('exerciseSearch', { _id: exercise.get().programmingLanguage }), false))
		});
})();
<|endoftext|>


<commit_before>
Ink.createModule('Ink.UI.DragDrop', 1, ['Ink.Dom.Element_1', 'Ink.Dom.Event_1', 'Ink.Dom.Css_1', 'Ink.Util.Array_1', 'Ink.UI.Common_1', 'Ink.Dom.Selector_1'], function(InkElement, InkEvent, InkCss, InkArray, UICommon, Selector){
    'use strict';

    function findElementUnderMouse(opt) {
        // TODO take advantage of getElementsFromPoint when it comes out
        opt.exceptFor.style.display = 'none';

        var ret = document.elementFromPoint(
            opt.x,
            opt.y);

        opt.exceptFor.style.display = '';

        return ret;
    }

    function DragDrop() {
        UICommon.BaseUIComponent.apply(this, arguments);
    }

    DragDrop._name = 'DragDrop_1';

    DragDrop._optionDefinition = {
        // dragdropContainer: ['Element', '.dragdrop-container'], - is this._element
        dragItem:       ['String', '.drag-item'],
        dragHandle:     ['String', '.drag-handle'],
        dropZone:       ['String', '.drop-zone'],
        ignoreDrag:     ['String', '.drag-ignore'],
        draggedCloneClass: ['String', 'drag-cloned-item'],
        placeholderClass: ['String', 'drag-placeholder-item'],
        onDrag:         ['Function', null],
        onDrop:         ['Function', null]
    };

    DragDrop.prototype = {
        /**
         * A replacement for Draggables, Droppables, and SortableList. It aims to be good at creating draggables, droppables and sortable lists at the same time while keeping it simple for everyone.
         *
         * A DragDrop component may contain one or more "dropZone"s, which are the areas where the "dragItem"s can be dropped. You can identify elements as being a dropZone or a dragItem by using the correct selectors (".drag-item" and ".drop-zone").
         *
         * @class Ink.UI.DragDrop
         * @constructor
         * @version 1
         * @param {Element} [element] Root element for the DragDrop. It can contain one or more dropzones.
         * @param {Object} [options]
         *  Options object, containing:
         * @param {String} [options.dragItem='.drag-item']
         *  Selector for the items to be dragged
         * @param {String} [options.dragHandle='.drag-handle']
         *  Selector for a dragging handle. You won't be able to drag other parts of the dragItem.
         * @param {String} [options.dropZone='.drop-zone']
         *  Selector of drop zones. Should add this to the element itself.
         * @param {String} [options.ignoreDrag='.drag-ignore']
         *  Selector of places where you can't drag.
         * @param {String} [options.draggedCloneClass='drag-cloned-item']
         *  Class for the cloned (and position:fixed'ed) element.
         * @param {String} [options.placeholderClass='drag-placeholder-item']
         *  Class for the placeholder clone
         * @param {Function} [options.onDrag]
         *  Called when dragging starts. Takes an `{ dragItem, dropZone }` object.
         * @param {Function} [options.onDrag]
         *  Called when dragging ends. Takes an `{ origin, dragItem, dropZone }` object.
         *
         * @sample Ink_UI_DragDrop_1.html
         **/
        _init: function() {
            this._dragActive = false;

            this._draggedElm = null;
            this._clonedElm = null;
            this._placeholderElm = null;
            this._originalDrop = null;

            this._mouseDelta = [0, 0];

            this._addEvents();
        },

        _addEvents: function() {
            InkEvent.on(this._element, 'mousedown touchstart', Ink.bindEvent(this._onMouseDown, this));
        },

        _onMouseDown: function(event) {
            var tgt = InkEvent.element(event);

            var draggedElm = InkElement.findUpwardsBySelector(tgt, this._options.dragItem);

            var elmIgnoreDraggable = InkElement.findUpwardsBySelector(tgt, this._options.ignoreDrag);

            if(draggedElm && !elmIgnoreDraggable) {
                // has handler
                var handleElm = Ink.s(this._options.dragHandle, draggedElm);
                if(handleElm && InkElement.findUpwardsBySelector(tgt, this._options.dragHandle)) {
                    this._dragActive = true;
                } else if (!handleElm) {
                    this._dragActive = true;
                }

                if (this._dragActive) {
                    InkEvent.stopDefault(event);
                    this._startDrag(event, draggedElm);
                }
            }
        },

        _startDrag: function(event, draggedElm) {
            // TODO rename
            this._clonedElm = draggedElm.cloneNode(true);
            this._placeholderElm = draggedElm.cloneNode(false);

            InkCss.addClassName(this._clonedElm, this._options.draggedCloneClass);
            this._clonedElm.removeAttribute('id');

            InkCss.addClassName(this._placeholderElm, this._options.placeholderClass);
            this._placeholderElm.removeAttribute('id');

            var rect = draggedElm.getBoundingClientRect();
            var dragElmDims = [
                rect.right - rect.left,
                rect.bottom - rect.top
            ];

            this._clonedElm.style.width = dragElmDims[0] + 'px';
            this._clonedElm.style.height = dragElmDims[1] + 'px';

            this._placeholderElm.style.width = dragElmDims[0] + 'px';
            this._placeholderElm.style.height = dragElmDims[1] + 'px';
            this._placeholderElm.style.visibility = 'hidden';

            // TODO goes in style
            this._clonedElm.style.position = 'fixed';
            this._clonedElm.style.zIndex = '1000';
            this._clonedElm.style.left = rect.left + 'px';
            this._clonedElm.style.top = rect.top + 'px';

            var mousePos = InkEvent.pointer(event);
            var dragElmPos = InkElement.offset(draggedElm);
            this._mouseDelta = [
                (mousePos.x - dragElmPos[0]),
                (mousePos.y - dragElmPos[1])
            ];

            this._clonedElm.style.opacity = '0.6';

            draggedElm.parentNode.insertBefore(this._clonedElm, draggedElm);

            // TODO rename
            this._draggedElm = draggedElm;

            draggedElm.parentNode.insertBefore(this._placeholderElm, draggedElm);
            InkCss.addClassName(draggedElm, 'hide-all');

            var hasOnDrag = typeof this._options.onDrag === 'function';
            var hasOnDrop = typeof this._options.onDrop === 'function';

            if (hasOnDrag || hasOnDrop) {
                var dragEvent = {
                    dragItem: this._draggedElm,
                    dropZone: this.getDropZone(this._draggedElm)
                };

                if (hasOnDrag) {
                    this._options.onDrag.call(this, dragEvent);
                }

                if (hasOnDrop) {
                    this._originalDrop = dragEvent.dropZone;
                }
            }

            var mouseMoveThrottled = InkEvent.throttle(this._onMouseMove, 50, {
                // Prevent the default of events
                preventDefault: true,
                bind: this
            });

            InkEvent.on(document, 'mousemove.inkdraggable touchmove.inkdraggable', mouseMoveThrottled);
            InkEvent.on(document, 'mouseup.inkdraggable touchend.inkdraggable',
                Ink.bindEvent(this._onMouseUp, this));
        },

        _onMouseMove: function(event) {
            if (!this._dragActive) { return; }

            var mousePos = InkEvent.pointer(event);

            var scrollLeft = InkElement.scrollWidth();
            var scrollTop = InkElement.scrollHeight();

            this._clonedElm.style.left =
                (mousePos.x - this._mouseDelta[0] - scrollLeft) + 'px';
            this._clonedElm.style.top =
                (mousePos.y - this._mouseDelta[1] - scrollTop) + 'px';

            var elUnderMouse = findElementUnderMouse({
                x: mousePos.x - scrollLeft,
                y: mousePos.y - scrollTop,
                exceptFor: this._clonedElm
            });

            var dropZoneUnderMouse =
                this.getDropZone(elUnderMouse);

            var isMyDropZone = dropZoneUnderMouse && (
                InkElement.isAncestorOf(this._element, dropZoneUnderMouse) ||
                this._element === dropZoneUnderMouse);

            if(dropZoneUnderMouse && isMyDropZone) {
                var otherDragItem =
                    InkElement.findUpwardsBySelector(elUnderMouse, this._options.dragItem);

                if (otherDragItem && this.isDragItem(otherDragItem)) {
                    // The mouse cursor is over another drag-item
                    this._insertPlaceholder(otherDragItem);
                } else if (this._dropZoneIsEmpty(dropZoneUnderMouse)) {
                    // The mouse cursor is over an empty dropzone, so there is nowhere to put it "after" or "before"
                    dropZoneUnderMouse.appendChild(this._placeholderElm);
                }
            }
            // Otherwise, the cursor is outside anything useful
        },

        /**
         * Returns whether a given .drag-item element is a plain old .drag-item element
         * and not one of the clones we're creating or the element we're really dragging.
         *
         * Used because the selector ".drag-item" finds these elements we don't consider drag-items
         *
         * @method isDragItem
         * @param elm {Element} The element to test.
         * @public
         **/
        isDragItem: function (elm) {
            return (
                Selector.matchesSelector(elm, this._options.dragItem) &&
                elm !== this._draggedElm &&
                elm !== this._placeholderElm &&
                elm !== this._clonedElm);
        },

        _dropZoneIsEmpty: function (dropZone) {
            // Find elements with the class .drag-item in the drop-zone
            var dragItems = Ink.ss(this._options.dragItem, dropZone);

            // Make sure none of these elements are actually the dragged element,
            // the placeholder, or the position:fixed clone.
            return !InkArray.some(dragItems, Ink.bindMethod(this, 'isDragItem'));
        },

        _onMouseUp: function() {
            if (!this._dragActive) { return; }

            // The actual dropping is just putting our *real* node where the placeholder once was.
            InkElement.insertBefore(this._draggedElm, this._placeholderElm);

            InkElement.remove(this._placeholderElm);
            InkElement.remove(this._clonedElm);

            InkCss.removeClassName(this._draggedElm, 'hide-all');

            InkEvent.off(document, '.inkdraggable');

            this._dragActive = false;

            if (typeof this._options.onDrop === 'function') {
                this._options.onDrop.call(this, {
                    origin: this._originalDrop,
                    dragItem: this._draggedElm,
                    dropZone: this.getDropZone(this._draggedElm)
                });
            }

            this._placeholderElm = null;
            this._clonedElm = null;
            this._draggedElm = null;
            this._originalDrop = null;
        },

        /**
         * Get the dropZone containing the given element.
         *
         * @method getDropZone
         * @param dragItem {Element} The dragItem to find the dropZone of
         * @returns {Element}
         * @public
         **/
        getDropZone: function (dragItem) {
            var ret = InkElement.findUpwardsBySelector(
                dragItem, this._options.dropZone) || this._element;

            if (InkElement.isAncestorOf(this._element, ret) || ret === this._element) {
                return ret;
            }

            return null;
        },

        /**
         * Returns what element the user is dragging, or `null` if no drag is occurring.
         *
         * @method getDraggedElement
         * @returns {Element|null} Element being dragged
         * @public
         **/
        getDraggedElement: function () {
            if (!this.dragActive) {
                return null;
            }

            return this._draggedElm;
        },

        /**
         * Called when mouse has moved over a new element
         *
         * Given a competitor drag-item, it figures out
         * whether we want to put our placeholder *after* it or *before* it.
         *
         **/
        _insertPlaceholder: function(elm) {
            var goesAfter = true;

            if (!InkArray.inArray(this._placeholderElm, InkElement.previousSiblings(elm))) {
                goesAfter = false;
            }

            if(goesAfter) {
                InkElement.insertAfter(this._placeholderElm, elm);
            } else {
                InkElement.insertBefore(this._placeholderElm, elm);
            }
        },

        /**
         * Destroy your DragDrop, removing it from the DOM
         *
         * @method destroy
         * @public
         * @returns {void}
         **/
        destroy: function () {
            if (this._dragActive) {
                InkEvent.off(document, '.inkdraggable');
            }
            UICommon.destroyComponent.call(this);
        }
    };

    UICommon.createUIComponent(DragDrop);

    return DragDrop;
});

<commit_msg>fix doc typo<commit_after>
Ink.createModule('Ink.UI.DragDrop', 1, ['Ink.Dom.Element_1', 'Ink.Dom.Event_1', 'Ink.Dom.Css_1', 'Ink.Util.Array_1', 'Ink.UI.Common_1', 'Ink.Dom.Selector_1'], function(InkElement, InkEvent, InkCss, InkArray, UICommon, Selector){
    'use strict';

    function findElementUnderMouse(opt) {
        // TODO take advantage of getElementsFromPoint when it comes out
        opt.exceptFor.style.display = 'none';

        var ret = document.elementFromPoint(
            opt.x,
            opt.y);

        opt.exceptFor.style.display = '';

        return ret;
    }

    function DragDrop() {
        UICommon.BaseUIComponent.apply(this, arguments);
    }

    DragDrop._name = 'DragDrop_1';

    DragDrop._optionDefinition = {
        // dragdropContainer: ['Element', '.dragdrop-container'], - is this._element
        dragItem:       ['String', '.drag-item'],
        dragHandle:     ['String', '.drag-handle'],
        dropZone:       ['String', '.drop-zone'],
        ignoreDrag:     ['String', '.drag-ignore'],
        draggedCloneClass: ['String', 'drag-cloned-item'],
        placeholderClass: ['String', 'drag-placeholder-item'],
        onDrag:         ['Function', null],
        onDrop:         ['Function', null]
    };

    DragDrop.prototype = {
        /**
         * A replacement for Draggables, Droppables, and SortableList. It aims to be good at creating draggables, droppables and sortable lists at the same time while keeping it simple for everyone.
         *
         * A DragDrop component may contain one or more "dropZone"s, which are the areas where the "dragItem"s can be dropped. You can identify elements as being a dropZone or a dragItem by using the correct selectors (".drag-item" and ".drop-zone").
         *
         * @class Ink.UI.DragDrop
         * @constructor
         * @version 1
         * @param {Element} [element] Root element for the DragDrop. It can contain one or more dropzones.
         * @param {Object} [options]
         *  Options object, containing:
         * @param {String} [options.dragItem='.drag-item']
         *  Selector for the items to be dragged
         * @param {String} [options.dragHandle='.drag-handle']
         *  Selector for a dragging handle. You won't be able to drag other parts of the dragItem.
         * @param {String} [options.dropZone='.drop-zone']
         *  Selector of drop zones. Should add this to the element itself.
         * @param {String} [options.ignoreDrag='.drag-ignore']
         *  Selector of places where you can't drag.
         * @param {String} [options.draggedCloneClass='drag-cloned-item']
         *  Class for the cloned (and position:fixed'ed) element.
         * @param {String} [options.placeholderClass='drag-placeholder-item']
         *  Class for the placeholder clone
         * @param {Function} [options.onDrag]
         *  Called when dragging starts. Takes an `{ dragItem, dropZone }` object.
         * @param {Function} [options.onDrop]
         *  Called when dragging ends. Takes an `{ origin, dragItem, dropZone }` object.
         *
         * @sample Ink_UI_DragDrop_1.html
         **/
        _init: function() {
            this._dragActive = false;

            this._draggedElm = null;
            this._clonedElm = null;
            this._placeholderElm = null;
            this._originalDrop = null;

            this._mouseDelta = [0, 0];

            this._addEvents();
        },

        _addEvents: function() {
            InkEvent.on(this._element, 'mousedown touchstart', Ink.bindEvent(this._onMouseDown, this));
        },

        _onMouseDown: function(event) {
            var tgt = InkEvent.element(event);

            var draggedElm = InkElement.findUpwardsBySelector(tgt, this._options.dragItem);

            var elmIgnoreDraggable = InkElement.findUpwardsBySelector(tgt, this._options.ignoreDrag);

            if(draggedElm && !elmIgnoreDraggable) {
                // has handler
                var handleElm = Ink.s(this._options.dragHandle, draggedElm);
                if(handleElm && InkElement.findUpwardsBySelector(tgt, this._options.dragHandle)) {
                    this._dragActive = true;
                } else if (!handleElm) {
                    this._dragActive = true;
                }

                if (this._dragActive) {
                    InkEvent.stopDefault(event);
                    this._startDrag(event, draggedElm);
                }
            }
        },

        _startDrag: function(event, draggedElm) {
            // TODO rename
            this._clonedElm = draggedElm.cloneNode(true);
            this._placeholderElm = draggedElm.cloneNode(false);

            InkCss.addClassName(this._clonedElm, this._options.draggedCloneClass);
            this._clonedElm.removeAttribute('id');

            InkCss.addClassName(this._placeholderElm, this._options.placeholderClass);
            this._placeholderElm.removeAttribute('id');

            var rect = draggedElm.getBoundingClientRect();
            var dragElmDims = [
                rect.right - rect.left,
                rect.bottom - rect.top
            ];

            this._clonedElm.style.width = dragElmDims[0] + 'px';
            this._clonedElm.style.height = dragElmDims[1] + 'px';

            this._placeholderElm.style.width = dragElmDims[0] + 'px';
            this._placeholderElm.style.height = dragElmDims[1] + 'px';
            this._placeholderElm.style.visibility = 'hidden';

            // TODO goes in style
            this._clonedElm.style.position = 'fixed';
            this._clonedElm.style.zIndex = '1000';
            this._clonedElm.style.left = rect.left + 'px';
            this._clonedElm.style.top = rect.top + 'px';

            var mousePos = InkEvent.pointer(event);
            var dragElmPos = InkElement.offset(draggedElm);
            this._mouseDelta = [
                (mousePos.x - dragElmPos[0]),
                (mousePos.y - dragElmPos[1])
            ];

            this._clonedElm.style.opacity = '0.6';

            draggedElm.parentNode.insertBefore(this._clonedElm, draggedElm);

            // TODO rename
            this._draggedElm = draggedElm;

            draggedElm.parentNode.insertBefore(this._placeholderElm, draggedElm);
            InkCss.addClassName(draggedElm, 'hide-all');

            var hasOnDrag = typeof this._options.onDrag === 'function';
            var hasOnDrop = typeof this._options.onDrop === 'function';

            if (hasOnDrag || hasOnDrop) {
                var dragEvent = {
                    dragItem: this._draggedElm,
                    dropZone: this.getDropZone(this._draggedElm)
                };

                if (hasOnDrag) {
                    this._options.onDrag.call(this, dragEvent);
                }

                if (hasOnDrop) {
                    this._originalDrop = dragEvent.dropZone;
                }
            }

            var mouseMoveThrottled = InkEvent.throttle(this._onMouseMove, 50, {
                // Prevent the default of events
                preventDefault: true,
                bind: this
            });

            InkEvent.on(document, 'mousemove.inkdraggable touchmove.inkdraggable', mouseMoveThrottled);
            InkEvent.on(document, 'mouseup.inkdraggable touchend.inkdraggable',
                Ink.bindEvent(this._onMouseUp, this));
        },

        _onMouseMove: function(event) {
            if (!this._dragActive) { return; }

            var mousePos = InkEvent.pointer(event);

            var scrollLeft = InkElement.scrollWidth();
            var scrollTop = InkElement.scrollHeight();

            this._clonedElm.style.left =
                (mousePos.x - this._mouseDelta[0] - scrollLeft) + 'px';
            this._clonedElm.style.top =
                (mousePos.y - this._mouseDelta[1] - scrollTop) + 'px';

            var elUnderMouse = findElementUnderMouse({
                x: mousePos.x - scrollLeft,
                y: mousePos.y - scrollTop,
                exceptFor: this._clonedElm
            });

            var dropZoneUnderMouse =
                this.getDropZone(elUnderMouse);

            var isMyDropZone = dropZoneUnderMouse && (
                InkElement.isAncestorOf(this._element, dropZoneUnderMouse) ||
                this._element === dropZoneUnderMouse);

            if(dropZoneUnderMouse && isMyDropZone) {
                var otherDragItem =
                    InkElement.findUpwardsBySelector(elUnderMouse, this._options.dragItem);

                if (otherDragItem && this.isDragItem(otherDragItem)) {
                    // The mouse cursor is over another drag-item
                    this._insertPlaceholder(otherDragItem);
                } else if (this._dropZoneIsEmpty(dropZoneUnderMouse)) {
                    // The mouse cursor is over an empty dropzone, so there is nowhere to put it "after" or "before"
                    dropZoneUnderMouse.appendChild(this._placeholderElm);
                }
            }
            // Otherwise, the cursor is outside anything useful
        },

        /**
         * Returns whether a given .drag-item element is a plain old .drag-item element
         * and not one of the clones we're creating or the element we're really dragging.
         *
         * Used because the selector ".drag-item" finds these elements we don't consider drag-items
         *
         * @method isDragItem
         * @param elm {Element} The element to test.
         * @public
         **/
        isDragItem: function (elm) {
            return (
                Selector.matchesSelector(elm, this._options.dragItem) &&
                elm !== this._draggedElm &&
                elm !== this._placeholderElm &&
                elm !== this._clonedElm);
        },

        _dropZoneIsEmpty: function (dropZone) {
            // Find elements with the class .drag-item in the drop-zone
            var dragItems = Ink.ss(this._options.dragItem, dropZone);

            // Make sure none of these elements are actually the dragged element,
            // the placeholder, or the position:fixed clone.
            return !InkArray.some(dragItems, Ink.bindMethod(this, 'isDragItem'));
        },

        _onMouseUp: function() {
            if (!this._dragActive) { return; }

            // The actual dropping is just putting our *real* node where the placeholder once was.
            InkElement.insertBefore(this._draggedElm, this._placeholderElm);

            InkElement.remove(this._placeholderElm);
            InkElement.remove(this._clonedElm);

            InkCss.removeClassName(this._draggedElm, 'hide-all');

            InkEvent.off(document, '.inkdraggable');

            this._dragActive = false;

            if (typeof this._options.onDrop === 'function') {
                this._options.onDrop.call(this, {
                    origin: this._originalDrop,
                    dragItem: this._draggedElm,
                    dropZone: this.getDropZone(this._draggedElm)
                });
            }

            this._placeholderElm = null;
            this._clonedElm = null;
            this._draggedElm = null;
            this._originalDrop = null;
        },

        /**
         * Get the dropZone containing the given element.
         *
         * @method getDropZone
         * @param dragItem {Element} The dragItem to find the dropZone of
         * @returns {Element}
         * @public
         **/
        getDropZone: function (dragItem) {
            var ret = InkElement.findUpwardsBySelector(
                dragItem, this._options.dropZone) || this._element;

            if (InkElement.isAncestorOf(this._element, ret) || ret === this._element) {
                return ret;
            }

            return null;
        },

        /**
         * Returns what element the user is dragging, or `null` if no drag is occurring.
         *
         * @method getDraggedElement
         * @returns {Element|null} Element being dragged
         * @public
         **/
        getDraggedElement: function () {
            if (!this.dragActive) {
                return null;
            }

            return this._draggedElm;
        },

        /**
         * Called when mouse has moved over a new element
         *
         * Given a competitor drag-item, it figures out
         * whether we want to put our placeholder *after* it or *before* it.
         *
         **/
        _insertPlaceholder: function(elm) {
            var goesAfter = true;

            if (!InkArray.inArray(this._placeholderElm, InkElement.previousSiblings(elm))) {
                goesAfter = false;
            }

            if(goesAfter) {
                InkElement.insertAfter(this._placeholderElm, elm);
            } else {
                InkElement.insertBefore(this._placeholderElm, elm);
            }
        },

        /**
         * Destroy your DragDrop, removing it from the DOM
         *
         * @method destroy
         * @public
         * @returns {void}
         **/
        destroy: function () {
            if (this._dragActive) {
                InkEvent.off(document, '.inkdraggable');
            }
            UICommon.destroyComponent.call(this);
        }
    };

    UICommon.createUIComponent(DragDrop);

    return DragDrop;
});

<|endoftext|>


<commit_before>var _ = require('underscore'),
    async = require('async'),
    FileMap = require('./util/file-map'),
    fu = require('./fileUtil'),
    ChildPool = require('./child-pool');

var uglify = new ChildPool(__dirname + '/uglify-worker');

exports.combine = function(context, files, output, minimize, noSeparator, callback) {

  function outputIfCompleted() {
    if (completed >= files.length) {
      var lastEl,
          map = new FileMap(output),
          warnings = [],

          tasks = [];

      _.each(content, function(el) {
          var content = el.content.toString();

          if (!noSeparator && (!lastEl || !lastEl.noSeparator) && map.content()) {
            map.add(undefined, '\n;;\n');
          }

          map.add(el.generated ? undefined : el.name, content, el);

          lastEl = el;
        }, '');

      var inputs = [];
      content.forEach(function(el) {
        if (el.inputs) {
          inputs.push.apply(inputs, el.inputs);
        } else if (el.name) {
          inputs.push(el.name);
        }
      });
      inputs = _.unique(inputs);

      // "Serialize" the data in the map
      tasks.push(function(callback) {
        callback(undefined, map.content());
      });

      // Minimize the content if flagged
      if (minimize) {
        var uglifyConfig = context.config.attributes.uglify || {};

        tasks.push(function(data, callback) {
          uglify.send({
              output: output,
              data: data,
              compressorOptions: uglifyConfig.compressor,
              manglerOptions: uglifyConfig.mangler,
              outputOptions: uglifyConfig.output,
              sourceMap: context.options.sourceMap ? map.sourceMap() : undefined
            },
            function(err, data) {
              if (err) {
                return callback(err);
              }

              _.each(data.warnings, function(msg) {
                var match = /(.*?)\s*\[.*:(\d+),(\d+)/.exec(msg);
                if (match) {
                  var msg = match[1],
                      line = parseInt(match[2], 10),
                      column = match[3],
                      context = map.context(line, column);

                  if (context && (!context.fileContext || !context.fileContext.ignoreWarnings)) {
                    context.msg = msg;
                    warnings.push(context);
                  }
                } else {
                  warnings.push({msg: msg});
                }
              });

              if (data.sourceMap) {
                // Remap the sourcemap output for the point that it is actually used for output
                // We need to restore the source map here as uglify will remove the original
                // Declaration
                map.sourceMap = function() { return data.sourceMap; };
              }

              callback(err, data.data);
            });
        });
      }

      // Output the source map if requested
      var sourceMap = context.options.sourceMap;
      if (sourceMap) {
        var inlineSourceMap = sourceMap === true;

        tasks.push(function(data, callback) {
          map.writeSourceMap({
            mapDestination: !inlineSourceMap && (sourceMap + '/' + context.buildPath),
            outputSource: inlineSourceMap,
            callback: function(err) {
              if (inlineSourceMap) {
                data += '\n' + map.sourceMapToken();
              }
              callback(err, data);
            }
          });
        });
      }

      // Output step
      tasks.push(function(data, callback) {
        fu.writeFile(output, data, callback);
      });

      // Excute everything and return to the caller
      async.waterfall(tasks, function(err) {
        if (err) {
          callback(new Error('Combined output "' + output + '" failed\n\t' + err));
          return;
        }

        callback(undefined, {
          fileName: output,
          inputs: inputs,
          warnings: warnings
        });
      });
    }
  }
  var completed = 0,
      content = [];

  files.forEach(function(resource) {
    var fileInfo = context.loadResource(resource, function(err) {
      if (err) {
        callback(err);
        return;
      }

      completed++;
      outputIfCompleted();
    });
    content.push(fileInfo);
  });
};
<commit_msg>Fix crash when multiple files are removed<commit_after>var _ = require('underscore'),
    async = require('async'),
    FileMap = require('./util/file-map'),
    fu = require('./fileUtil'),
    ChildPool = require('./child-pool');

var uglify = new ChildPool(__dirname + '/uglify-worker');

exports.combine = function(context, files, output, minimize, noSeparator, callback) {

  function outputIfCompleted() {
    if (completed >= files.length) {
      var lastEl,
          map = new FileMap(output),
          warnings = [],

          tasks = [];

      _.each(content, function(el) {
          var content = el.content.toString();

          if (!noSeparator && (!lastEl || !lastEl.noSeparator) && map.content()) {
            map.add(undefined, '\n;;\n');
          }

          map.add(el.generated ? undefined : el.name, content, el);

          lastEl = el;
        }, '');

      var inputs = [];
      content.forEach(function(el) {
        if (el.inputs) {
          inputs.push.apply(inputs, el.inputs);
        } else if (el.name) {
          inputs.push(el.name);
        }
      });
      inputs = _.unique(inputs);

      // "Serialize" the data in the map
      tasks.push(function(callback) {
        callback(undefined, map.content());
      });

      // Minimize the content if flagged
      if (minimize) {
        var uglifyConfig = context.config.attributes.uglify || {};

        tasks.push(function(data, callback) {
          uglify.send({
              output: output,
              data: data,
              compressorOptions: uglifyConfig.compressor,
              manglerOptions: uglifyConfig.mangler,
              outputOptions: uglifyConfig.output,
              sourceMap: context.options.sourceMap ? map.sourceMap() : undefined
            },
            function(err, data) {
              if (err) {
                return callback(err);
              }

              _.each(data.warnings, function(msg) {
                var match = /(.*?)\s*\[.*:(\d+),(\d+)/.exec(msg);
                if (match) {
                  var msg = match[1],
                      line = parseInt(match[2], 10),
                      column = match[3],
                      context = map.context(line, column);

                  if (context && (!context.fileContext || !context.fileContext.ignoreWarnings)) {
                    context.msg = msg;
                    warnings.push(context);
                  }
                } else {
                  warnings.push({msg: msg});
                }
              });

              if (data.sourceMap) {
                // Remap the sourcemap output for the point that it is actually used for output
                // We need to restore the source map here as uglify will remove the original
                // Declaration
                map.sourceMap = function() { return data.sourceMap; };
              }

              callback(err, data.data);
            });
        });
      }

      // Output the source map if requested
      var sourceMap = context.options.sourceMap;
      if (sourceMap) {
        var inlineSourceMap = sourceMap === true;

        tasks.push(function(data, callback) {
          map.writeSourceMap({
            mapDestination: !inlineSourceMap && (sourceMap + '/' + context.buildPath),
            outputSource: inlineSourceMap,
            callback: function(err) {
              if (inlineSourceMap) {
                data += '\n' + map.sourceMapToken();
              }
              callback(err, data);
            }
          });
        });
      }

      // Output step
      tasks.push(function(data, callback) {
        fu.writeFile(output, data, callback);
      });

      // Excute everything and return to the caller
      async.waterfall(tasks, function(err) {
        if (err) {
          callback(new Error('Combined output "' + output + '" failed\n\t' + err));
          return;
        }

        callback(undefined, {
          fileName: output,
          inputs: inputs,
          warnings: warnings
        });
      });
    }
  }
  var completed = 0,
      content = [];

  files.forEach(function(resource) {
    var fileInfo = context.loadResource(resource, function(err) {
      if (err && callback) {
        callback(err);
        callback = undefined;
        return;
      }

      if (callback) {
        completed++;
        outputIfCompleted();
      }
    });
    content.push(fileInfo);
  });
};
<|endoftext|>


<commit_before>(function () {
    var XHR_DONE_STATE = 4;

    var HTTP_AUTHENTICATION_ERROR = 401;

    /**
     * The error code returned by HOD when there is no SSO cookie.
     * @type {number}
     */
    var NO_USER_TOKEN_CODE = 12102;

    /**
     * Build a URL encoded query string from a map of key to value.
     * @param {Object.<string, string>} parameters
     * @return {string}
     */
    function buildQueryString(parameters) {
        return Object.keys(parameters).map(function (name) {
            return encodeURIComponent(name) + '=' + encodeURIComponent(parameters[name]);
        }).join('&');
    }

    /**
     * Add a ready state change listener to an XMLHttpRequest. When the request is done, the callback is called. Assumes
     * a JSON response.
     * @param {XMLHttpRequest} xhr
     * @param {Function} callback Called with an error response and status if there is one or null and the parsed response
     */
    function addReadyStateChangeListener(xhr, callback) {
        xhr.addEventListener('readystatechange', function () {
            if (xhr.readyState === XHR_DONE_STATE) {
                var response;

                try {
                    response = JSON.parse(xhr.response);
                } catch (e) {
                    response = null;
                }

                if (xhr.status === 200) {
                    callback(null, response);
                } else {
                    callback(xhr.status, response);
                }
            }
        });
    }

    /**
     * Make a signed request to HOD, calling the callback when it is done.
     * @param {SignedRequest} request
     * @param {Function} callback
     */
    function makeSignedRequest(request, callback) {
        var xhr = new XMLHttpRequest();
        xhr.withCredentials = true;
        xhr.open(request.verb, request.url);
        xhr.setRequestHeader('token', request.token);

        if (request.body) {
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        }

        addReadyStateChangeListener(xhr, callback);

        if (request.body) {
            xhr.send(request.body);
        } else {
            xhr.send();
        }
    }

    /**
     * Get a request to HOD signed by the application backend.
     * @param url
     * @param callback Called with an error if there was one or null and a {@link SignedRequest}.
     */
    function getSignedRequest(url, callback) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        addReadyStateChangeListener(xhr, callback);
        xhr.send();
    }

    /**
     * @typedef {Object} AuthenticateCombinedOutput
     * @property {string} domain The application domain
     * @property {string} application The application name
     * @property {string} username
     * @property {Object} combinedToken
     */
    /**
     * Represents a request to make to HOD from the browser. Should be generated on the server using the application
     * unbound token.
     * @typedef {Object} SignedRequest
     * @property {string} url Full URL for the request, including protocol, host, path and query string
     * @property {string} verb HTTP method
     * @property {string} token HMAC signing of the request
     * @property {string} body Request body if there is one, null if not
     */
    /**
     * @typedef {Object} AuthenticateCombinedOptions
     * @property {string} applicationRoot Root path of application
     * @property {string} [hodDomain] HOD Domain, defaults to idolondemand.com
     * @property {string} [ssoPage] URL of HOD SSO page, defaults to https://idolondemand.com/sso.html. In case that provided, overrides the hodDomain value for the SSO page redirection.
     * @property {SignedRequest} [listApplicationRequest] A signed request to get a list of applications
     * @property {string} [combinedRequestApi=/api/combined-request] The URI to obtain the signed authentication request from
     * @property {string} [listApplicationRequestApi=/api/list-application-request] The URI to obtain the signed list application request from
     */
    /**
     * Attempt to authenticate the user using SSO. If the user is not signed in, redirects the browser to the SSO page.
     * If the authentication fails, the callback is called with an error. If not, it is called with null and a combined
     * token.
     * @param {Function} callback Called with an error if there was one, or with null and a {@link AuthenticateCombinedOutput}.
     * @param {AuthenticateCombinedOptions} options Configuration options
     */
    function authenticateCombined(callback, options) {
        options = options || {};
        var applicationRoot = options.applicationRoot;
        var hodDomain = options.hodDomain || 'idolondemand.com';
        var ssoPage = options.ssoPage || ['https://', hodDomain, '/sso.html'].join('');
        var combinedRequestApi = options.combinedRequestApi || '/api/combined-request';
        var listApplicationRequestApi = options.listApplicationRequestApi || '/api/list-application-request';

        function handleHodErrorResponse(error, response, callback) {
            if (response && response.error === NO_USER_TOKEN_CODE) {
                // The user SSO token is (probably) invalid so redirect to SSO
                window.location = ssoPage + '?' + buildQueryString({redirect_url: window.location});
            } else {
                callback(error);
            }
        }

        function authenticate(listApplicationRequest) {
            // Get a list of applications and users which match the authentication
            makeSignedRequest(listApplicationRequest, function (error, response) {
                if (error) {
                    handleHodErrorResponse(error, response, callback);
                } else if (!response.length || !response[0].users.length) { // for a case when there is no user authorized for this application
                    handleHodErrorResponse(HTTP_AUTHENTICATION_ERROR, null, callback);
                } else {
                    // TODO: Allow user to choose application and user store names and domains
                    var domain = response[0].domain;
                    var application = response[0].name;
                    var username = response[0].users[0].name;

                    var combinedTokenParameters = {
                        domain: domain,
                        application: application,
                        'user-store-domain': response[0].users[0].domain,
                        'user-store-name': response[0].users[0].userStore
                    };

                    getSignedRequest(applicationRoot + combinedRequestApi + '?' + buildQueryString(combinedTokenParameters), function (error, combinedRequest) {
                        if (error) {
                            callback(error);
                        } else {
                            // Obtain a combined token
                            makeSignedRequest(combinedRequest, function (error, response) {
                                if (error) {
                                    handleHodErrorResponse(error, response, callback);
                                } else {
                                    var combinedToken = response.token;

                                    callback(null, {
                                        application: application,
                                        domain: domain,
                                        username: username,
                                        combinedToken: combinedToken
                                    });
                                }
                            });
                        }
                    });
                }
            });
        }

        if (options.listApplicationRequest) {
            authenticate(options.listApplicationRequest);
        } else {
            getSignedRequest(applicationRoot + listApplicationRequestApi, function (error, listApplicationRequest) {
                if (error) {
                    callback(error);
                } else {
                    authenticate(listApplicationRequest);
                }
            });
        }
    }

    window.havenOnDemandSso = window.havenOnDemandSso || {};
    window.havenOnDemandSso.authenticate = authenticateCombined;
})();
<commit_msg>Changed defaults from idolondemand to havenondemand. [rev: alex.scown]<commit_after>(function () {
    var XHR_DONE_STATE = 4;

    var HTTP_AUTHENTICATION_ERROR = 401;

    /**
     * The error code returned by HOD when there is no SSO cookie.
     * @type {number}
     */
    var NO_USER_TOKEN_CODE = 12102;

    /**
     * Build a URL encoded query string from a map of key to value.
     * @param {Object.<string, string>} parameters
     * @return {string}
     */
    function buildQueryString(parameters) {
        return Object.keys(parameters).map(function (name) {
            return encodeURIComponent(name) + '=' + encodeURIComponent(parameters[name]);
        }).join('&');
    }

    /**
     * Add a ready state change listener to an XMLHttpRequest. When the request is done, the callback is called. Assumes
     * a JSON response.
     * @param {XMLHttpRequest} xhr
     * @param {Function} callback Called with an error response and status if there is one or null and the parsed response
     */
    function addReadyStateChangeListener(xhr, callback) {
        xhr.addEventListener('readystatechange', function () {
            if (xhr.readyState === XHR_DONE_STATE) {
                var response;

                try {
                    response = JSON.parse(xhr.response);
                } catch (e) {
                    response = null;
                }

                if (xhr.status === 200) {
                    callback(null, response);
                } else {
                    callback(xhr.status, response);
                }
            }
        });
    }

    /**
     * Make a signed request to HOD, calling the callback when it is done.
     * @param {SignedRequest} request
     * @param {Function} callback
     */
    function makeSignedRequest(request, callback) {
        var xhr = new XMLHttpRequest();
        xhr.withCredentials = true;
        xhr.open(request.verb, request.url);
        xhr.setRequestHeader('token', request.token);

        if (request.body) {
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        }

        addReadyStateChangeListener(xhr, callback);

        if (request.body) {
            xhr.send(request.body);
        } else {
            xhr.send();
        }
    }

    /**
     * Get a request to HOD signed by the application backend.
     * @param url
     * @param callback Called with an error if there was one or null and a {@link SignedRequest}.
     */
    function getSignedRequest(url, callback) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        addReadyStateChangeListener(xhr, callback);
        xhr.send();
    }

    /**
     * @typedef {Object} AuthenticateCombinedOutput
     * @property {string} domain The application domain
     * @property {string} application The application name
     * @property {string} username
     * @property {Object} combinedToken
     */
    /**
     * Represents a request to make to HOD from the browser. Should be generated on the server using the application
     * unbound token.
     * @typedef {Object} SignedRequest
     * @property {string} url Full URL for the request, including protocol, host, path and query string
     * @property {string} verb HTTP method
     * @property {string} token HMAC signing of the request
     * @property {string} body Request body if there is one, null if not
     */
    /**
     * @typedef {Object} AuthenticateCombinedOptions
     * @property {string} applicationRoot Root path of application
     * @property {string} [hodDomain] HOD Domain, defaults to havenondemand.com
     * @property {string} [ssoPage] URL of HOD SSO page, defaults to https://dev.havenondemand.com/sso.html. In case that provided, overrides the hodDomain value for the SSO page redirection.
     * @property {SignedRequest} [listApplicationRequest] A signed request to get a list of applications
     * @property {string} [combinedRequestApi=/api/combined-request] The URI to obtain the signed authentication request from
     * @property {string} [listApplicationRequestApi=/api/list-application-request] The URI to obtain the signed list application request from
     */
    /**
     * Attempt to authenticate the user using SSO. If the user is not signed in, redirects the browser to the SSO page.
     * If the authentication fails, the callback is called with an error. If not, it is called with null and a combined
     * token.
     * @param {Function} callback Called with an error if there was one, or with null and a {@link AuthenticateCombinedOutput}.
     * @param {AuthenticateCombinedOptions} options Configuration options
     */
    function authenticateCombined(callback, options) {
        options = options || {};
        var applicationRoot = options.applicationRoot;
        var hodDomain = options.hodDomain || 'havenondemand.com';
        var ssoPage = options.ssoPage || 'https://dev.' + hodDomain + '/sso.html';
        var combinedRequestApi = options.combinedRequestApi || '/api/combined-request';
        var listApplicationRequestApi = options.listApplicationRequestApi || '/api/list-application-request';

        function handleHodErrorResponse(error, response, callback) {
            if (response && response.error === NO_USER_TOKEN_CODE) {
                // The user SSO token is (probably) invalid so redirect to SSO
                window.location = ssoPage + '?' + buildQueryString({redirect_url: window.location});
            } else {
                callback(error);
            }
        }

        function authenticate(listApplicationRequest) {
            // Get a list of applications and users which match the authentication
            makeSignedRequest(listApplicationRequest, function (error, response) {
                if (error) {
                    handleHodErrorResponse(error, response, callback);
                } else if (!response.length || !response[0].users.length) { // for a case when there is no user authorized for this application
                    handleHodErrorResponse(HTTP_AUTHENTICATION_ERROR, null, callback);
                } else {
                    // TODO: Allow user to choose application and user store names and domains
                    var domain = response[0].domain;
                    var application = response[0].name;
                    var username = response[0].users[0].name;

                    var combinedTokenParameters = {
                        domain: domain,
                        application: application,
                        'user-store-domain': response[0].users[0].domain,
                        'user-store-name': response[0].users[0].userStore
                    };

                    getSignedRequest(applicationRoot + combinedRequestApi + '?' + buildQueryString(combinedTokenParameters), function (error, combinedRequest) {
                        if (error) {
                            callback(error);
                        } else {
                            // Obtain a combined token
                            makeSignedRequest(combinedRequest, function (error, response) {
                                if (error) {
                                    handleHodErrorResponse(error, response, callback);
                                } else {
                                    var combinedToken = response.token;

                                    callback(null, {
                                        application: application,
                                        domain: domain,
                                        username: username,
                                        combinedToken: combinedToken
                                    });
                                }
                            });
                        }
                    });
                }
            });
        }

        if (options.listApplicationRequest) {
            authenticate(options.listApplicationRequest);
        } else {
            getSignedRequest(applicationRoot + listApplicationRequestApi, function (error, listApplicationRequest) {
                if (error) {
                    callback(error);
                } else {
                    authenticate(listApplicationRequest);
                }
            });
        }
    }

    window.havenOnDemandSso = window.havenOnDemandSso || {};
    window.havenOnDemandSso.authenticate = authenticateCombined;
})();
<|endoftext|>


<commit_before>// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// 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.

/**
 * @fileoverview Definition of goog.messaging.RespondingChannel, which wraps a
 * MessageChannel and allows the user to get the response from the services.
 *
 */

goog.provide('goog.messaging.RespondingChannel');

goog.require('goog.Disposable');
goog.require('goog.debug.Logger');
goog.require('goog.messaging.MessageChannel'); // interface
goog.require('goog.messaging.MultiChannel');
goog.require('goog.messaging.MultiChannel.VirtualChannel');

/**
 * Creates a new RespondingChannel wrapping a single MessageChannel.
 * @param {goog.messaging.MessageChannel} messageChannel The messageChannel to
 *     to wrap and allow for responses. This channel must not have any existing
 *     services registered. All service registration must be done through the
 *     {@link RespondingChannel#registerService} api instead. The other end of
 *     channel must also be a RespondingChannel.
 * @constructor
 * @extends {goog.Disposable}
 */
goog.messaging.RespondingChannel = function(messageChannel) {
  goog.base(this);

  /**
   * The message channel wrapped in a MultiChannel so we can send private and
   * public messages on it.
   * @type {goog.messaging.MultiChannel}
   * @private
   */
  this.messageChannel_ = new goog.messaging.MultiChannel(messageChannel);

  /**
   * Map of invocation signatures to function callbacks. These are used to keep
   * track of the asyncronous service invocations so the result of a service
   * call can be passed back to a callback in the calling frame.
   * @type {Object.<number, function(Object)>}
   * @private
   */
  this.sigCallbackMap_ = {};

  /**
   * The virtual channel to send private messages on.
   * @type {goog.messaging.MultiChannel.VirtualChannel}
   * @private
   */
  this.privateChannel_ = this.messageChannel_.createVirtualChannel(
      goog.messaging.RespondingChannel.PRIVATE_CHANNEL_);

  /**
   * The virtual channel to send public messages on.
   * @type {goog.messaging.MultiChannel.VirtualChannel}
   * @private
   */
  this.publicChannel_ = this.messageChannel_.createVirtualChannel(
      goog.messaging.RespondingChannel.PUBLIC_CHANNEL_);

  this.privateChannel_.registerService(
      goog.messaging.RespondingChannel.CALLBACK_SERVICE_,
      goog.bind(this.callbackServiceHandler_, this),
      true);
};
goog.inherits(goog.messaging.RespondingChannel, goog.Disposable);

/**
 * The name of the method invocation callback service (used internally).
 * @type {string}
 * @const
 * @private
 */
goog.messaging.RespondingChannel.CALLBACK_SERVICE_ = 'mics';

/**
 * The name of the channel to send private control messages on.
 * @type {string}
 * @const
 * @private
 */
goog.messaging.RespondingChannel.PRIVATE_CHANNEL_ = 'private';

/**
 * The name of the channel to send public messages on.
 * @type {string}
 * @const
 * @private
 */
goog.messaging.RespondingChannel.PUBLIC_CHANNEL_ = 'public';

/**
 * The next signature index to save the callback against.
 * @type {number}
 * @private
 */
goog.messaging.RespondingChannel.prototype.nextSignatureIndex_ = 0;

/**
 * Logger object for goog.messaging.RespondingChannel.
 * @type {goog.debug.Logger}
 * @private
 */
goog.messaging.RespondingChannel.prototype.logger_ =
    goog.debug.Logger.getLogger('goog.messaging.RespondingChannel');

/**
 * Gets a random number to use for method invocation results.
 * @return {number} A unique random signature.
 * @private
 */
goog.messaging.RespondingChannel.prototype.getNextSignature_ = function() {
  return this.nextSignatureIndex_++;
};

/** @override */
goog.messaging.RespondingChannel.prototype.disposeInternal = function() {
  goog.dispose(this.messageChannel_);
  delete this.messageChannel_;
  delete this.publicChannel_;
  delete this.privateChannel_;
};

/**
 * Sends a message over the channel.
 * @param {string} serviceName The name of the service this message should be
 *     delivered to.
 * @param {string|!Object} payload The value of the message. If this is an
 *     Object, it is serialized to a string before sending if necessary.
 * @param {function(?Object)} callback The callback invoked with
 *     the result of the service call.
 */
goog.messaging.RespondingChannel.prototype.send = function(
    serviceName,
    payload,
    callback) {

  var signature = this.getNextSignature_();
  this.sigCallbackMap_[signature] = callback;

  var message = {};
  message['signature'] = signature;
  message['data'] = payload;

  this.publicChannel_.send(serviceName, message);
};

/**
 * Receives the results of the peer's service results.
 * @param {!Object|string} message The results from the remote service
 *     invocation.
 * @private
 */
goog.messaging.RespondingChannel.prototype.callbackServiceHandler_ = function(
    message) {

  var signature = message['signature'];
  var result = message['data'];

  if (signature in this.sigCallbackMap_) {
    var callback = /** @type {function(Object)} */ this.sigCallbackMap_[
        signature];
    callback(result);
    delete this.sigCallbackMap_[signature];
  } else {
    this.logger_.warning('Received signature is invalid');
  }
};

/**
 * Registers a service to be called when a message is received.
 * @param {string} serviceName The name of the service.
 * @param {function(!Object)} callback The callback to process the
 *     incoming messages. Passed the payload.
 */
goog.messaging.RespondingChannel.prototype.registerService = function(
    serviceName, callback) {
  this.publicChannel_.registerService(
      serviceName,
      goog.bind(this.callbackProxy_, this, callback),
      true);
};

/**
 * A intermediary proxy for service callbacks to be invoked and return their
 * their results to the remote caller's callback.
 * @param {function((string|!Object))} callback The callback to process the
 *     incoming messages. Passed the payload.
 * @param {!Object|string} message The message containing the signature and
 *     the data to invoke the service callback with.
 * @private
 */
goog.messaging.RespondingChannel.prototype.callbackProxy_ = function(
    callback, message) {

  var resultMessage = {};
  resultMessage['data'] = callback(message['data']);
  resultMessage['signature'] = message['signature'];

  this.privateChannel_.send(
      goog.messaging.RespondingChannel.CALLBACK_SERVICE_,
      resultMessage);
};
<commit_msg><commit_after>// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// 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.

/**
 * @fileoverview Definition of goog.messaging.RespondingChannel, which wraps a
 * MessageChannel and allows the user to get the response from the services.
 *
 */

goog.provide('goog.messaging.RespondingChannel');

goog.require('goog.Disposable');
goog.require('goog.debug.Logger');
goog.require('goog.messaging.MessageChannel'); // interface
goog.require('goog.messaging.MultiChannel');
goog.require('goog.messaging.MultiChannel.VirtualChannel');

/**
 * Creates a new RespondingChannel wrapping a single MessageChannel.
 * @param {goog.messaging.MessageChannel} messageChannel The messageChannel to
 *     to wrap and allow for responses. This channel must not have any existing
 *     services registered. All service registration must be done through the
 *     {@link RespondingChannel#registerService} api instead. The other end of
 *     channel must also be a RespondingChannel.
 * @constructor
 * @extends {goog.Disposable}
 */
goog.messaging.RespondingChannel = function(messageChannel) {
  goog.base(this);

  /**
   * The message channel wrapped in a MultiChannel so we can send private and
   * public messages on it.
   * @type {goog.messaging.MultiChannel}
   * @private
   */
  this.messageChannel_ = new goog.messaging.MultiChannel(messageChannel);

  /**
   * Map of invocation signatures to function callbacks. These are used to keep
   * track of the asyncronous service invocations so the result of a service
   * call can be passed back to a callback in the calling frame.
   * @type {Object.<number, function(Object)>}
   * @private
   */
  this.sigCallbackMap_ = {};

  /**
   * The virtual channel to send private messages on.
   * @type {goog.messaging.MultiChannel.VirtualChannel}
   * @private
   */
  this.privateChannel_ = this.messageChannel_.createVirtualChannel(
      goog.messaging.RespondingChannel.PRIVATE_CHANNEL_);

  /**
   * The virtual channel to send public messages on.
   * @type {goog.messaging.MultiChannel.VirtualChannel}
   * @private
   */
  this.publicChannel_ = this.messageChannel_.createVirtualChannel(
      goog.messaging.RespondingChannel.PUBLIC_CHANNEL_);

  this.privateChannel_.registerService(
      goog.messaging.RespondingChannel.CALLBACK_SERVICE_,
      goog.bind(this.callbackServiceHandler_, this),
      true);
};
goog.inherits(goog.messaging.RespondingChannel, goog.Disposable);

/**
 * The name of the method invocation callback service (used internally).
 * @type {string}
 * @const
 * @private
 */
goog.messaging.RespondingChannel.CALLBACK_SERVICE_ = 'mics';

/**
 * The name of the channel to send private control messages on.
 * @type {string}
 * @const
 * @private
 */
goog.messaging.RespondingChannel.PRIVATE_CHANNEL_ = 'private';

/**
 * The name of the channel to send public messages on.
 * @type {string}
 * @const
 * @private
 */
goog.messaging.RespondingChannel.PUBLIC_CHANNEL_ = 'public';

/**
 * The next signature index to save the callback against.
 * @type {number}
 * @private
 */
goog.messaging.RespondingChannel.prototype.nextSignatureIndex_ = 0;

/**
 * Logger object for goog.messaging.RespondingChannel.
 * @type {goog.debug.Logger}
 * @private
 */
goog.messaging.RespondingChannel.prototype.logger_ =
    goog.debug.Logger.getLogger('goog.messaging.RespondingChannel');

/**
 * Gets a random number to use for method invocation results.
 * @return {number} A unique random signature.
 * @private
 */
goog.messaging.RespondingChannel.prototype.getNextSignature_ = function() {
  return this.nextSignatureIndex_++;
};

/** @override */
goog.messaging.RespondingChannel.prototype.disposeInternal = function() {
  goog.dispose(this.messageChannel_);
  delete this.messageChannel_;
  // Note: this.publicChannel_ and this.privateChannel_ get disposed by
  //     this.messageChannel_
  delete this.publicChannel_;
  delete this.privateChannel_;
};

/**
 * Sends a message over the channel.
 * @param {string} serviceName The name of the service this message should be
 *     delivered to.
 * @param {string|!Object} payload The value of the message. If this is an
 *     Object, it is serialized to a string before sending if necessary.
 * @param {function(?Object)} callback The callback invoked with
 *     the result of the service call.
 */
goog.messaging.RespondingChannel.prototype.send = function(
    serviceName,
    payload,
    callback) {

  var signature = this.getNextSignature_();
  this.sigCallbackMap_[signature] = callback;

  var message = {};
  message['signature'] = signature;
  message['data'] = payload;

  this.publicChannel_.send(serviceName, message);
};

/**
 * Receives the results of the peer's service results.
 * @param {!Object|string} message The results from the remote service
 *     invocation.
 * @private
 */
goog.messaging.RespondingChannel.prototype.callbackServiceHandler_ = function(
    message) {

  var signature = message['signature'];
  var result = message['data'];

  if (signature in this.sigCallbackMap_) {
    var callback = /** @type {function(Object)} */ this.sigCallbackMap_[
        signature];
    callback(result);
    delete this.sigCallbackMap_[signature];
  } else {
    this.logger_.warning('Received signature is invalid');
  }
};

/**
 * Registers a service to be called when a message is received.
 * @param {string} serviceName The name of the service.
 * @param {function(!Object)} callback The callback to process the
 *     incoming messages. Passed the payload.
 */
goog.messaging.RespondingChannel.prototype.registerService = function(
    serviceName, callback) {
  this.publicChannel_.registerService(
      serviceName,
      goog.bind(this.callbackProxy_, this, callback),
      true);
};

/**
 * A intermediary proxy for service callbacks to be invoked and return their
 * their results to the remote caller's callback.
 * @param {function((string|!Object))} callback The callback to process the
 *     incoming messages. Passed the payload.
 * @param {!Object|string} message The message containing the signature and
 *     the data to invoke the service callback with.
 * @private
 */
goog.messaging.RespondingChannel.prototype.callbackProxy_ = function(
    callback, message) {

  var resultMessage = {};
  resultMessage['data'] = callback(message['data']);
  resultMessage['signature'] = message['signature'];
  // The callback invoked above may have disposed the channel so check if it
  // exists.
  if (this.privateChannel_) {
    this.privateChannel_.send(
        goog.messaging.RespondingChannel.CALLBACK_SERVICE_,
        resultMessage);
  }
};
<|endoftext|>


<commit_before>import React from 'react';
import { Link } from 'react-router';

class BlockUser extends React.Component {
  render() {
    return (
      <Link to="/conversation" className="c-block-user">

9 new messages

17:31

Firstname Lastname

Lorem ipsum dolor sit amet.

      </Link>
    );
  }

  // functions.
}

export default BlockUser;
<commit_msg>add props to block-user component<commit_after>import React from 'react';
import { Link } from 'react-router';

class BlockUser extends React.Component {
  render() {
    return (
      <Link to="/conversation" className="c-block-user" key={ this.props.id }>

          { this.renderBlockUserData() }

{ this.props.name }

{ this.renderBlockUserText() }

      </Link>
    );
  }

  renderBlockUserData() {
    let data = [];

    if (this.props.unreadMessagesLength) {
      data.push(
{this.props.unreadMessagesLength} new messages
);
    }

    if (this.props.lastMessage) {
      data.push(
{this.props.lastMessageDate}
);
    } else if (this.props.lastSeenDate) {
      data.push(
{ this.props.lastSeenDate }
);
    }

    return data;
  }

  renderBlockUserText() {
    let text;

    if (this.props.lastMessage) {
      text = this.props.lastMessage;
    }

    else if (this.props.description) {
      text = this.props.description;
    }

    return text;
  }
}

BlockUser.propTypes = {
  id: React.PropTypes.number,
  name: React.PropTypes.string,
  description: React.PropTypes.string,
  lastMessage: React.PropTypes.string,
  imageSource: React.PropTypes.string,
  lastSeenDate: React.PropTypes.string,
  lastMessageDate: React.PropTypes.string,
  unreadMessagesLength: React.PropTypes.number,
};

export default BlockUser;
<|endoftext|>


<commit_before>'use strict';
var L = require('leaflet');
var tangram = require('./tangram');

var MapControl = L.Map.extend({
  options: {},
  // overriding Leaflet's map initializer
  initialize: function (element, options) {
    L.Map.prototype.initialize.call(this, element, L.extend({}, L.Map.prototype.options, options));
    // Set Icon path manually
    L.Icon.Default.imagePath = this._getImagePath();

    // overriding double click behaviour to zoom up where it is clicked
    this.doubleClickZoom.disable();
    this.on('dblclick', function (e) {
      this.setView(e.latlng, this.getZoom() + 1);
    });
    this._checkConditions(false);
    // Tangram script is being injected,
    L.DomEvent.on(tangram.scriptEl, 'load', this._setupScene, this);
  },

  _getImagePath: function () {
    // Modified Leaflet's Image Path function
    var scripts = document.getElementsByTagName('script');
    var leafletRe = /[\/^]mapzen[\-\._]?([\w\-\._]*)\.js\??/;

    var i, len, src, matches, path;

    for (i = 0, len = scripts.length; i < len; i++) {
      src = scripts[i].src;
      matches = src.match(leafletRe);

      if (matches) {
        path = src.split(leafletRe)[0];
        return (path ? path + '/' : '') + 'images';
      }
    }
  },

  _setupScene: function () {
    if (this.options.scene) {
      if (this._hasWebGL()) {
        switch (this.options.scene) {
          // Set up Mapzen house style
          case 'zinc':
          case 'refill':
          case 'cinnabar':
            Tangram.leafletLayer({
              scene: 'https://mapzen.com/carto/' + this.options.scene + '-style/' + this.options.scene + '-style.yaml',
              attribution: 'Tangram | © OSM contributors | Mapzen'
            }).addTo(this);
            break;
          case 'bubble-wrap':
            Tangram.leafletLayer({
              scene: 'https://mapzen.com/carto/' + this.options.scene + '-style/' + this.options.scene + '.yaml',
              attribution: 'Tangram | © OSM contributors | Mapzen'
            }).addTo(this);
            break;
          default:
            Tangram.leafletLayer({
              scene: this.options.scene,
              attribution: 'Tangram | © OSM contributors | Mapzen'
            }).addTo(this);
            break;
        }
      } else {
        console.log('WebGL is not available, falling back to OSM default tile.');
        L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
          attribution: '© OpenStreetMap contributors'
        }).addTo(this);
      }
    }
  },

  _checkConditions: function (force) {
    if (this._isThisIframed()) {
      // do not scroll zoom when it is iframed
      this.scrollWheelZoom = false;
      var anchors = document.querySelectorAll('a');

      for (var i = 0, j = anchors.length; i < j; i++) {
        var el = anchors[i];
        // Only set target when not explicitly specified
        // to avoid overwriting intentional targeting behavior
        // Unless the force parameter is true, then targets of
        // '_blank' are forced to to be '_top'
        if (!el.target || (force === true && el.target === '_blank')) {
          el.target = '_top';
        }
      }
    }
    // do not show zoom control buttons on mobile
    // need to add more check to detect touch device
    if ('ontouchstart' in window) {
      this._disableZoomControl();
    }
  },

  _hasWebGL: function () {
    try {
      var canvas = document.createElement('canvas');
      return !!(window.WebGLRenderingContext && (canvas.getContext('webgl') || canvas.getContext('experimental-webgl')));
    } catch (x) {
      return false;
    }
  },

  _isThisIframed: function () {
    return (window.self === window.top);
  },

  _disableZoomControl: function () {
    this.zoomControl._container.style.display = 'none';
  }
});

module.exports = MapControl;

module.exports.map = function (element, options) {
  return new MapControl(element, options);
};
<commit_msg>made mistake on resolving conflicts, put the right one back<commit_after>'use strict';
var L = require('leaflet');
var tangram = require('./tangram');

var MapControl = L.Map.extend({
  options: {},
  // overriding Leaflet's map initializer
  initialize: function (element, options) {
    L.Map.prototype.initialize.call(this, element, L.extend({}, L.Map.prototype.options, options));
    // Set Icon path manually
    L.Icon.Default.imagePath = this._getImagePath();

    // overriding double click behaviour to zoom up where it is clicked
    this.doubleClickZoom.disable();
    this.on('dblclick', function (e) {
      this.setView(e.latlng, this.getZoom() + 1);
    });
    this._checkConditions(false);
    // Tangram script is being injected,
    L.DomEvent.on(tangram.scriptEl, 'load', this._setupScene, this);
  },

  _getImagePath: function () {
    // Modified Leaflet's Image Path function
    var scripts = document.getElementsByTagName('script');
    var leafletRe = /[\/^]mapzen[\-\._]?([\w\-\._]*)\.js\??/;

    var i, len, src, matches, path;

    for (i = 0, len = scripts.length; i < len; i++) {
      src = scripts[i].src;
      matches = src.match(leafletRe);

      if (matches) {
        path = src.split(leafletRe)[0];
        return (path ? path + '/' : '') + 'images';
      }
    }
  },

  _setupScene: function () {
    if (this._hasWebGL()) {
      console.log('given scene:', this.options.scene);
      console.log('using scene:', (this.options.scene || L.Mapzen.HouseStyles.BubbleWrap));
      Tangram.leafletLayer({
        scene: (this.options.scene || L.Mapzen.HouseStyles.BubbleWrap),
        attribution: 'Tangram | © OSM contributors | Mapzen'
      }).addTo(this);
    } else {
      // When WebGL is not avilable
      console.log('WebGL is not available, falling back to OSM default tile.');
      L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        attribution: '© OpenStreetMap contributors'
      }).addTo(this);
    }
  },

  _checkConditions: function (force) {
    if (this._isThisIframed()) {
      // do not scroll zoom when it is iframed
      this.scrollWheelZoom = false;
      var anchors = document.querySelectorAll('a');

      for (var i = 0, j = anchors.length; i < j; i++) {
        var el = anchors[i];
        // Only set target when not explicitly specified
        // to avoid overwriting intentional targeting behavior
        // Unless the force parameter is true, then targets of
        // '_blank' are forced to to be '_top'
        if (!el.target || (force === true && el.target === '_blank')) {
          el.target = '_top';
        }
      }
    }
    // do not show zoom control buttons on mobile
    // need to add more check to detect touch device
    if ('ontouchstart' in window) {
      this._disableZoomControl();
    }
  },

  _hasWebGL: function () {
    try {
      var canvas = document.createElement('canvas');
      return !!(window.WebGLRenderingContext && (canvas.getContext('webgl') || canvas.getContext('experimental-webgl')));
    } catch (x) {
      return false;
    }
  },

  _isThisIframed: function () {
    return (window.self === window.top);
  },

  _disableZoomControl: function () {
    this.zoomControl._container.style.display = 'none';
  }
});

module.exports = MapControl;

module.exports.map = function (element, options) {
  return new MapControl(element, options);
};
<|endoftext|>