<commit_before>var utils = require("@/common/utils");
var _const = require("@/common/const");
var uikit = require("./uikit");
var apiHelper = require("./apis");
var channel = require("./channel");
var profile = require("@/app/tools/profile");
var eventListener = require("@/app/tools/eventListener");
var loading = require("./uikit/loading");
var getToHost = require("@/app/common/transfer");

var dom;
var starsUl;
var resolvedBtn;
var commentDom;
var tagContainer;
var dialog;

var starList;

var session;
var invite;
var score;
var evaluationDegreeId;
var isSingleTag;
var resolvedDom;
var resolveTip;
var resolvedId = 1;
var _initOnce = _.once(_init);
var evaluateType; // 评价方式
var sessionResolved;// 问题解决评价
var fiveStarState;//默认五星评价
var defaultScore;

module.exports = {
	init: init,
	show: show,
};

function _init(){
	loading.show("satisfaction");
	//默认五星评价的开关
	apiHelper.getDefaultFiveStarEnable()
	.then(function(resp){
		fiveStarState = resp;
	});
	apiHelper.getSatisfactionTipWord().then(function(tipWord){
		dom = sessionResolved ? utils.createElementFromHTML([
			"
",
			"
" + resolveTip + "",
			"
" + __("evaluation.resolved") + "",
			"" + __("evaluation.unsolved") + "

",
			"" + tipWord + "",
			"

",
			"

",
			"<textarea spellcheck=\"false\" placeholder=\"" + __("evaluation.review") + "\"></textarea>",
			"
"
		].join(""))
			:
			utils.createElementFromHTML([
				"
",
				"" + tipWord + "",
				"

",
				"

",
				"<textarea spellcheck=\"false\" placeholder=\"" + __("evaluation.review") + "\"></textarea>",
				"
"
			].join(""));
		starsUl = dom.querySelector("ul");
		commentDom = dom.querySelector("textarea");
		tagContainer = dom.querySelector(".tag-container");
		resolvedBtn = dom.querySelectorAll(".resolve-btn");
		resolvedDom = dom.querySelector(".resolved");

		utils.live(".resolve-btn", "click", function(){
			utils.removeClass(resolvedBtn, "selected");
			utils.addClass(this, "selected");
			resolvedId = this.dataset.num;
			if(fiveStarState){
				if(resolvedId == 1){
					utils.addClass(starList, "sel");
					score = defaultScore;
				}
				else{
					utils.removeClass(starList, "sel");
					score = false;
				}
			}
		});

		utils.live("li", "click", function(){
			var level = +this.getAttribute("data-level");

			evaluationDegreeId = this.getAttribute("data-evaluate-id");
			score = this.getAttribute("data-score");
			isSingleTag = this.getAttribute("data-isSingleTag");
			level && _.each(starList, function(elem, i){
				utils.toggleClass(elem, "sel", i < level);
			});

			evaluationDegreeId && _createLabel(evaluationDegreeId);
		}, starsUl);

		utils.live("span.tag", "click", function(){
			var selectedTagNodeList = tagContainer.querySelectorAll(".selected");
			if(isSingleTag == "true"){
				utils.removeClass(selectedTagNodeList, "selected");
				utils.toggleClass(this, "selected");
			}
			else{
				utils.toggleClass(this, "selected");
			}

		}, tagContainer);

		dialog = uikit.createDialog({
			contentDom: dom,
			className: "satisfaction"
		}).addButton({
			confirmText: __("common.submit"),
			confirm: _confirm,
		});
		loading.hide("satisfaction");
		dialog.show();

		// 火狐浏览器 _setSatisfaction时找不到starsUl，所以必须先执行完init
		_setSatisfaction();
	});
}

function _clear(){
	commentDom.blur();
	commentDom.value = "";
	score = null;
	// clear stars
	utils.removeClass(starList, "sel");
	// clear label
	tagContainer.innerHTML = "";
	// clear resolvedBtn
	utils.removeClass(resolvedBtn, "selected");
	utils.addClass(resolvedDom, "selected");
	resolvedId = 1;

}

function _sendSatisfaction(score, content, session, invite, appraiseTags, resolutionParam, evaluationDegreeId){
	var data = {
		ext: {
			weichat: {
				ctrlType: "enquiry",
				ctrlArgs: {
					// 后端类型要求，inviteId必须传数字
					inviteId: invite || 0,
					serviceSessionId: session || profile.currentOfficialAccount.sessionId || "",
					detail: content,
					summary: score,
					appraiseTags: appraiseTags,
					resolutionParam: resolutionParam,
					evaluationDegreeId: evaluationDegreeId,
					// 评价方式，由前端传入：visitor - 访客主动评价; agent - 坐席邀请; system - 强制邀请,访客点击关闭窗口或会话结束
					evaluateWay: evaluateType
				}
			}
		}
	};
	if(!sessionResolved){
		delete data.ext.weichat.ctrlArgs.resolutionParam;
	}
	channel.sendText("", data);
}

function _setSatisfaction(){
	apiHelper.getEvaluationDegrees().then(function(entities){
		var labelID;
		var lastScore;
		starsUl.innerHTML = _.chain(entities)
		.sortBy("level")
		.map(function(elem, index){
			// stat level 1-based
			var level = index + 1;
			var name = elem.name;
			var id = elem.id;
			var score = elem.score;
			var isSingleTag = elem.isSingleTag;
			labelID = id;
			lastScore = score;

			return "
H
";
		})
		.value()
		.join("");

		starList = starsUl.querySelectorAll("li");
		defaultScore = lastScore
		if(fiveStarState){
			if(resolvedId == 1){
				utils.addClass(starList, "sel");
				//创建评价标签
				_createLabel(labelID);
				score = defaultScore;
			}
			else{
				utils.removeClass(starList, "sel");
				score = false;
			}
		}

	});
}

function _createLabel(evaluateId){
	apiHelper.getAppraiseTags(evaluateId).then(function(entities){
		tagContainer.innerHTML = _.map(entities, function(elem){
			var name = elem.name;
			var id = elem.id;

			return "" + name + "";
		}).join("");
		utils.removeClass(tagContainer, "hide");
	});
}

function _confirm(){
	var selectedTagNodeList = tagContainer.querySelectorAll(".selected");
	var tagNodeList = tagContainer.querySelectorAll(".tag");
	var content = commentDom.value;
	var appraiseTags = _.map(selectedTagNodeList, function(elem){
		return {
			id: elem.getAttribute("data-label-id"),
			name: elem.innerText
		};
	});
	var resolutionParam = [{
		id: resolvedId,
		name: resolvedId == 1 ? "已解决" : "未解决",
		score: resolvedId,
		resolutionParamTags: []
	}];

	// 必须选择星级
	if(!score){
		uikit.tip(__("evaluation.select_level_please"));
		// 防止对话框关闭
		return false;
	}
	// 若有标签则至少选择一个
	else if(tagNodeList.length > 0 && selectedTagNodeList.length === 0){
		uikit.tip(__("evaluation.select_tag_please"));
		// 防止对话框关闭
		return false;
	}

	_sendSatisfaction(score, content, session, invite, appraiseTags, resolutionParam, evaluationDegreeId);
	uikit.showSuccess(__("evaluation.submit_success"));
	getToHost.send({ event: _const.EVENTS.EVALUATIONSUBMIT });
	// 强制评价点击确定关闭会话框
	setTimeout(function(){
		// 关闭会话
		if(evaluateType === "system"){
			// 取消轮询接口
			eventListener.trigger(_const.SYSTEM_EVENT.CHAT_CLOSED);
			profile.currentOfficialAccount.sessionId && apiHelper.closeChatDialog({ serviceSessionId: profile.currentOfficialAccount.sessionId });
			getToHost.send({ event: _const.EVENTS.CLOSE });
		}
	}, 2000);
	_clear();
}

function show(inviteId, serviceSessionId, evaluateWay){

	apiHelper.getEvaluteSolveWord().then(function(tip){
		resolveTip = tip;
	});
	apiHelper.getServiceSessionResolved()
	.then(function(resp){
		sessionResolved = resp;
		_initOnce();
	});

	session = serviceSessionId;
	invite = inviteId;
	evaluateType = evaluateWay;

	dialog && dialog.show();
}

function init(){
	eventListener.add(
		_const.SYSTEM_EVENT.SATISFACTION_EVALUATION_MESSAGE_RECEIVED,
		function(officialAccount, inviteId, serviceSessionId){
			if(officialAccount !== profile.currentOfficialAccount) return;
			show(inviteId, serviceSessionId, "system");
		}
	);
}
<commit_msg>Revert "Revert "CLOUD-20603【敦煌网】网页渠道评价默认选中五星问题""<commit_after>var utils = require("@/common/utils");
var _const = require("@/common/const");
var uikit = require("./uikit");
var apiHelper = require("./apis");
var channel = require("./channel");
var profile = require("@/app/tools/profile");
var eventListener = require("@/app/tools/eventListener");
var loading = require("./uikit/loading");
var getToHost = require("@/app/common/transfer");

var dom;
var starsUl;
var resolvedBtn;
var commentDom;
var tagContainer;
var dialog;

var starList;

var session;
var invite;
var score;
var evaluationDegreeId;
var isSingleTag;
var resolvedDom;
var resolveTip;
var resolvedId = 1;
var _initOnce = _.once(_init);
var evaluateType; // 评价方式
var sessionResolved;// 问题解决评价
var fiveStarState;//默认五星评价
var defaultScore;

module.exports = {
	init: init,
	show: show,
};

function _init(){
	loading.show("satisfaction");
	//默认五星评价的开关
	apiHelper.getDefaultFiveStarEnable()
	.then(function(resp){
		fiveStarState = resp;
	});
	apiHelper.getSatisfactionTipWord().then(function(tipWord){
		dom = sessionResolved ? utils.createElementFromHTML([
			"
",
			"
" + resolveTip + "",
			"
" + __("evaluation.resolved") + "",
			"" + __("evaluation.unsolved") + "

",
			"" + tipWord + "",
			"

",
			"

",
			"<textarea spellcheck=\"false\" placeholder=\"" + __("evaluation.review") + "\"></textarea>",
			"
"
		].join(""))
			:
			utils.createElementFromHTML([
				"
",
				"" + tipWord + "",
				"

",
				"

",
				"<textarea spellcheck=\"false\" placeholder=\"" + __("evaluation.review") + "\"></textarea>",
				"
"
			].join(""));
		starsUl = dom.querySelector("ul");
		commentDom = dom.querySelector("textarea");
		tagContainer = dom.querySelector(".tag-container");
		resolvedBtn = dom.querySelectorAll(".resolve-btn");
		resolvedDom = dom.querySelector(".resolved");

		utils.live(".resolve-btn", "click", function(){
			utils.removeClass(resolvedBtn, "selected");
			utils.addClass(this, "selected");
			resolvedId = this.dataset.num;
			if(fiveStarState){
				if(resolvedId == 1){
					utils.addClass(starList, "sel");
					score = defaultScore;
				}
				else{
					utils.removeClass(starList, "sel");
					score = false;
				}
			}
		});

		utils.live("li", "click", function(){
			var level = +this.getAttribute("data-level");

			evaluationDegreeId = this.getAttribute("data-evaluate-id");
			score = this.getAttribute("data-score");
			isSingleTag = this.getAttribute("data-isSingleTag");
			level && _.each(starList, function(elem, i){
				utils.toggleClass(elem, "sel", i < level);
			});

			evaluationDegreeId && _createLabel(evaluationDegreeId);
		}, starsUl);

		utils.live("span.tag", "click", function(){
			var selectedTagNodeList = tagContainer.querySelectorAll(".selected");
			if(isSingleTag == "true"){
				utils.removeClass(selectedTagNodeList, "selected");
				utils.toggleClass(this, "selected");
			}
			else{
				utils.toggleClass(this, "selected");
			}

		}, tagContainer);

		dialog = uikit.createDialog({
			contentDom: dom,
			className: "satisfaction"
		}).addButton({
			confirmText: __("common.submit"),
			confirm: _confirm,
		});
		loading.hide("satisfaction");
		dialog.show();

		// 火狐浏览器 _setSatisfaction时找不到starsUl，所以必须先执行完init
		_setSatisfaction();
	});
}

function _clear(){
	commentDom.blur();
	commentDom.value = "";
	score = null;
	// clear stars
	utils.removeClass(starList, "sel");
	// clear label
	tagContainer.innerHTML = "";
	// clear resolvedBtn
	utils.removeClass(resolvedBtn, "selected");
	utils.addClass(resolvedDom, "selected");
	resolvedId = 1;

}

function _sendSatisfaction(score, content, session, invite, appraiseTags, resolutionParam, evaluationDegreeId){
	var data = {
		ext: {
			weichat: {
				ctrlType: "enquiry",
				ctrlArgs: {
					// 后端类型要求，inviteId必须传数字
					inviteId: invite || 0,
					serviceSessionId: session || profile.currentOfficialAccount.sessionId || "",
					detail: content,
					summary: score,
					appraiseTags: appraiseTags,
					resolutionParam: resolutionParam,
					evaluationDegreeId: evaluationDegreeId,
					// 评价方式，由前端传入：visitor - 访客主动评价; agent - 坐席邀请; system - 强制邀请,访客点击关闭窗口或会话结束
					evaluateWay: evaluateType
				}
			}
		}
	};
	if(!sessionResolved){
		delete data.ext.weichat.ctrlArgs.resolutionParam;
	}
	channel.sendText("", data);
}

function _setSatisfaction(){
	apiHelper.getEvaluationDegrees().then(function(entities){
		var labelID;
		var lastScore;
		starsUl.innerHTML = _.chain(entities)
		.sortBy("level")
		.map(function(elem, index){
			// stat level 1-based
			var level = index + 1;
			var name = elem.name;
			var id = elem.id;
			var score = elem.score;
			var isSingleTag = elem.isSingleTag;
			labelID = id;
			lastScore = score;

			return "
H
";
		})
		.value()
		.join("");

		starList = starsUl.querySelectorAll("li");
		defaultScore = lastScore
		if(fiveStarState){
			if(resolvedId == 1){
				utils.addClass(starList, "sel");
				//创建评价标签
				_createLabel(labelID);
				score = defaultScore;
			}
			else{
				utils.removeClass(starList, "sel");
				score = false;
			}
		}

	});
}

function _createLabel(evaluateId){
	apiHelper.getAppraiseTags(evaluateId).then(function(entities){
		tagContainer.innerHTML = _.map(entities, function(elem){
			var name = elem.name;
			var id = elem.id;

			return "" + name + "";
		}).join("");
		utils.removeClass(tagContainer, "hide");
	});
}

function _confirm(){
	var selectedTagNodeList = tagContainer.querySelectorAll(".selected");
	var tagNodeList = tagContainer.querySelectorAll(".tag");
	var content = commentDom.value;
	var appraiseTags = _.map(selectedTagNodeList, function(elem){
		return {
			id: elem.getAttribute("data-label-id"),
			name: elem.innerText
		};
	});
	var resolutionParam = [{
		id: resolvedId,
		name: resolvedId == 1 ? "已解决" : "未解决",
		score: resolvedId,
		resolutionParamTags: []
	}];

	// 必须选择星级
	if(!score){
		uikit.tip(__("evaluation.select_level_please"));
		// 防止对话框关闭
		return false;
	}
	// 若有标签则至少选择一个
	else if(tagNodeList.length > 0 && selectedTagNodeList.length === 0){
		uikit.tip(__("evaluation.select_tag_please"));
		// 防止对话框关闭
		return false;
	}

	_sendSatisfaction(score, content, session, invite, appraiseTags, resolutionParam, evaluationDegreeId);
	uikit.showSuccess(__("evaluation.submit_success"));
	getToHost.send({ event: _const.EVENTS.EVALUATIONSUBMIT });
	// 强制评价点击确定关闭会话框
	setTimeout(function(){
		// 关闭会话
		if(evaluateType === "system"){
			// 取消轮询接口
			eventListener.trigger(_const.SYSTEM_EVENT.CHAT_CLOSED);
			profile.currentOfficialAccount.sessionId && apiHelper.closeChatDialog({ serviceSessionId: profile.currentOfficialAccount.sessionId });
			getToHost.send({ event: _const.EVENTS.CLOSE });
		}
	}, 2000);
	_clear();
}

function show(inviteId, serviceSessionId, evaluateWay){

	apiHelper.getEvaluteSolveWord().then(function(tip){
		resolveTip = tip;
	});
	apiHelper.getServiceSessionResolved()
	.then(function(resp){
		sessionResolved = resp;
		_initOnce();
	});

	session = serviceSessionId;
	invite = inviteId;
	evaluateType = evaluateWay;
	_setDefaultScore();
	dialog && dialog.show();
}

function _setDefaultScore(){
	if(!starsUl){
		return false;
	}
	starList = starsUl.querySelectorAll("li");
	if(fiveStarState){
		if(resolvedId == 1){
			utils.addClass(starList, "sel");
			score = defaultScore;
		}
		else{
			utils.removeClass(starList, "sel");
			score = false;
		}
	}
}

function init(){
	eventListener.add(
		_const.SYSTEM_EVENT.SATISFACTION_EVALUATION_MESSAGE_RECEIVED,
		function(officialAccount, inviteId, serviceSessionId){
			if(officialAccount !== profile.currentOfficialAccount) return;
			show(inviteId, serviceSessionId, "system");
		}
	);
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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.
 */

import {htmlTemplate} from './gr-delete-repo_html.js';

class GrDeleteRepo extends Polymer.Element {
  /** @returns {string} name of the component */
  static get is() { return 'gr-delete-repo'; }

  /** @returns {?} template for this component */
  static get template() { return htmlTemplate; }

  /**
   * Defines properties of the component
   *
   * @returns {?}
   */
  static get properties() {
    return {
      repoName: String,
      config: Object,
      action: Object,
      actionId: String,
    };
  }

  connectedCallback() {
    super.connectedCallback();
    this.actionId = this.plugin.getPluginName() + '~delete';
    this.action = this.config.actions[this.actionId];
    this.hidden = !this.action;
  }

  _handleCommandTap() {
    this.$.deleteRepoOverlay.open();
  }

  _handleCloseDeleteRepo() {
    this.$.deleteRepoOverlay.close();
  }

  _handleDeleteRepo() {
    const endpoint = '/projects/' +
        encodeURIComponent(this.repoName) + '/' +
        this.actionId;

    const json = {
      force: this.$.forceDeleteOpenChangesCheckBox.checked,
      preserve: this.$.preserveGitRepoCheckBox.checked,
    };

    const errFn = response => {
      this.fire('page-error', {response});
    };

    return this.plugin.restApi().send(
        this.action.method, endpoint, json, errFn)
        .then(r => {
          this.plugin.restApi().invalidateReposCache();
          Gerrit.Nav.navigateToRelativeUrl('/admin/repos');
        });
  }
}

customElements.define(GrDeleteRepo.is, GrDeleteRepo);
<commit_msg>Fix event handling on error<commit_after>/**
 * @license
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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.
 */

import {htmlTemplate} from './gr-delete-repo_html.js';

class GrDeleteRepo extends Polymer.Element {
  /** @returns {string} name of the component */
  static get is() { return 'gr-delete-repo'; }

  /** @returns {?} template for this component */
  static get template() { return htmlTemplate; }

  /**
   * Defines properties of the component
   *
   * @returns {?}
   */
  static get properties() {
    return {
      repoName: String,
      config: Object,
      action: Object,
      actionId: String,
    };
  }

  connectedCallback() {
    super.connectedCallback();
    this.actionId = this.plugin.getPluginName() + '~delete';
    this.action = this.config.actions[this.actionId];
    this.hidden = !this.action;
  }

  _handleCommandTap() {
    this.$.deleteRepoOverlay.open();
  }

  _handleCloseDeleteRepo() {
    this.$.deleteRepoOverlay.close();
  }

  _handleDeleteRepo() {
    const endpoint = '/projects/' +
        encodeURIComponent(this.repoName) + '/' +
        this.actionId;

    const json = {
      force: this.$.forceDeleteOpenChangesCheckBox.checked,
      preserve: this.$.preserveGitRepoCheckBox.checked,
    };

    const errFn = response => {
      this.dispatchEvent(new CustomEvent('page-error', {
        detail: {response},
        bubbles: true,
        composed: true,
      }));
    };

    return this.plugin.restApi().send(
        this.action.method, endpoint, json, errFn)
        .then(r => {
          this.plugin.restApi().invalidateReposCache();
          Gerrit.Nav.navigateToRelativeUrl('/admin/repos');
        });
  }
}

customElements.define(GrDeleteRepo.is, GrDeleteRepo);
<|endoftext|>


<commit_before>
// MarkerController is reponsible for event handling such as moving to and opening the POI when selected.
class MarkerController {

    constructor(searchbar, markerController, mapController) {
        this._onPoiCardClickedCallback = null;

        this._eegeoPoiSourceIdMap = {};
        this._markerController = markerController;
        this._mapController = mapController;

        searchbar.on("searchresultselect", (event) => { this.goToResult(event); });
    }

    goToResult(event) {
        this._markerController.selectMarker(event.result.sourceId);
        this._mapController.getMap().setView(event.result.location.latLng, 15);
        this._openPoiView(event.result.resultId, event.result);
    }

    _openPoiView(markerId, poi) {
        // Here we create our POI view from the poi data. You may want to add your own POI view here instead.
        const element = PoiViewContainer(poi);
        let location = poi.location;
        const marker = this._markerController.getMarker(markerId);

        if (marker) {
            marker.closePopup();
        }

         this._mapController.addPopupWithOffsetForPois(location, element);

        const options = {
            durationSeconds: 0.0,
            allowInterruption: true
        }
    }
}
<commit_msg>Fix for MPLy-10287 Can now click on markers to open POIs. Buddy: Jean<commit_after>
// MarkerController is reponsible for event handling such as moving to and opening the POI when selected.
class MarkerController {

    constructor(searchbar, markerController, mapController) {
        this._onPoiCardClickedCallback = null;

        this._eegeoPoiSourceIdMap = {};
        this._markerController = markerController;
        this._mapController = mapController;

        searchbar.on("searchresultselect", (event) => { this.goToResult(event); });
        searchbar.on("searchresultsupdate", (event)=> { this.updateMarkers(event) });
    }

    goToResult(event) {
        this._markerController.selectMarker(event.result.sourceId);
        this._mapController.getMap().setView(event.result.location.latLng, 15);
        this._openPoiView(event.result.resultId, event.result);
    }

    updateMarkers(event) {
        let markerIDs = this._markerController.getAllMarkerIds();
        for (var i = 0; i < markerIDs.length; ++i)
        {
            const marker = this._markerController.getMarker(markerIDs[i]);
            marker.on("click", () => {
                var id = marker.id;
                if(event.results[id])
                {
                    this._openPoiView(id, event.results[id]);
                }
            });
        }
    }

    _openPoiView(markerId, poi) {
        // Here we create our POI view from the poi data. You may want to add your own POI view here instead.
        const element = PoiViewContainer(poi);
        let location = poi.location;
        const marker = this._markerController.getMarker(markerId);

        if (marker) {
            marker.closePopup();
        }

         this._mapController.addPopupWithOffsetForPois(location, element);

        const options = {
            durationSeconds: 0.0,
            allowInterruption: true
        }
    }
}
<|endoftext|>


<commit_before>/** MIT License (c) copyright B Cavalier & J Hann */

(function(define) {
define(function() {

	var methodsToForward = ['add', 'update', 'remove'];

	return function(adapter1, adapter2) {

		var unwatch1, unwatch2;

		unwatch1 = initForwarding(adapter1, adapter2);
		unwatch2 = initForwarding(adapter2, adapter1);

		return function() {
			unwatch1();
			unwatch2();
		};
	};

	function createForwarder(method) {
		function doForward(target, item, index) {
			this.forwardTo = noop;
			target[method](item, index);
			this.forwardTo = doForward;
		}

		return {
			forwardTo: doForward
		};
	}

	function createCallback(forwarder, to) {
		return function(item, index) {
			forwarder.forwardTo(to, item, index);
		}
	}

	function initForwarding(from, to) {
		var forwarder, callbacks, i, len;

		callbacks = [];
		for(i = 0, len = methodsToForward.length; i < len; i++) {
			forwarder = createForwarder(methodsToForward[i]);
			callbacks.push(createCallback(forwarder, to));
		}

		return from.watch.apply(from, callbacks);
	}

	function noop() {}

});
}(
	typeof define == 'function'
		? define
		: function(factory) { module.exports = factory(); }
));
<commit_msg>restored: correct method names and mediates objects returned from itemAdded or itemUpdated<commit_after>/** MIT License (c) copyright B Cavalier & J Hann */

(function(define) {
define(function(require) {

	var methodsToForward, ObjectMediator;

	methodsToForward = ['itemAdded', 'itemUpdated', 'itemRemoved'];

	ObjectMediator = require('./SimpleMediator');

	return function (adapter1, adapter2) {

		var unwatch1, unwatch2, unwatchObjects;

		unwatchObjects = createUnwatcher();
		unwatch1 = initForwarding(adapter1, adapter2, unwatchObjects);
		unwatch2 = initForwarding(adapter2, adapter1, unwatchObjects);

		return function () {
			unwatch1();
			unwatch2();
			unwatchObjects();
		};
	};

	function createForwarder(method) {
		function doForward(target, item, index) {
			var newItem;
			this.forwardTo = noop;
			newItem = target[method](item, index);
			this.forwardTo = doForward;
			return newItem;
		}

		return {
			forwardTo: doForward
		};
	}

	function createCallback(forwarder, to) {
		return function (item, index) {
			var newItem;
			newItem = forwarder.forwardTo(to, item, index);
			// if the forwarded function created a related item, we
			// mediate a relationship with the original item
			if (newItem) {
				forwarder.unwatcher.push(ObjectMediator(item, newItem));
			}
		}
	}

	function initForwarding(from, to, itemUnwatcher) {
		var forwarder, callbacks, i, len;

		callbacks = [];
		for (i = 0, len = methodsToForward.length; i < len; i++) {
			forwarder = createForwarder(methodsToForward[i]);
			forwarder.unwatcher = itemUnwatcher;
			callbacks.push(createCallback(forwarder, to));
		}

		return from.watch.apply(from, callbacks);
	}

	function createUnwatcher() {
		var unwatchers;
		unwatchers = [];
		function unwatchAll() {
			var unwatch;
			while ((unwatch = unwatchers.pop())) unwatch();
		}
		unwatchAll.push = function addUnwatcher(unwatcher) {
			unwatchers.push(unwatcher);
		};
		return unwatchAll;
	}

	function noop() {}

});
}(
	typeof define == 'function'
		? define
		: function(factory) { module.exports = factory(require); }
));
<|endoftext|>


<commit_before>/*!
    Copyright (C) 2016 Google Inc.
    Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
*/

(function (can, $, GGRC) {
  GGRC.Components('relevantFilter', {
    tag: 'relevant-filter',
    template: can.view(GGRC.mustache_path + '/mapper/relevant_filter.mustache'),
    scope: {
      define: {
        disableCreate: {
          type: 'boolean',
          'default': false
        }
      },
      relevant_menu_item: '@',
      show_all: '@',
      addFilter: function () {
        var menu = this.attr('menu');

        if (this.attr('relevant_menu_item') === 'parent' &&
             Number(this.attr('panel_number')) !== 0 &&
             !this.attr('has_parent')) {
          menu.unshift({
            title_singular: 'Previous objects',
            model_singular: '__previous__'
          });
        }

        this.attr('relevant').push({
          value: false,
          filter: new can.Map(),
          menu: menu,
          model_name: menu[0].model_singular
        });
      },
      menu: can.compute(function () {
        var type = this.attr('type');
        var mappings;
        var models;
        if (/true/i.test(this.attr('show_all'))) {
          // find all widget types and manually add Cycle since it's missing
          // convert names to CMS models and prune invalid (undefined)
          models = ['Cycle'];
          models = Array.prototype.concat.apply(models,
              _.values(GGRC.tree_view.base_widgets_by_type));
          models = _.difference(_.unique(models),
                               ['CycleTaskEntry', 'CycleTaskGroupObject']);
          models = _.map(models, function (mapping) {
            return CMS.Models[mapping];
          });
          return _.sortBy(_.compact(models), 'model_singular');
        }

        if (type === 'AllObject') {
          type = GGRC.page_model.type;
        }
        mappings = GGRC.Mappings.get_canonical_mappings_for(type);
        return _.sortBy(_.compact(_.map(_.keys(mappings), function (mapping) {
          return CMS.Models[mapping];
        })), 'model_singular');
      })
    },
    events: {
      init: function () {
        this.setRelevant();
      },
      setRelevant: function () {
        this.scope.attr('relevant').replace([]);
        can.each(this.scope.attr('relevantTo') || [], function (item) {
          var model = CMS.Models[item.type].cache[item.id];
          this.scope.attr('relevant').push({
            value: true,
            filter: model,
            menu: this.scope.attr('menu'),
            model_name: model.constructor.shortName
          });
        }, this);
      },
      '.ui-autocomplete-input autocomplete:select': function (el, ev, data) {
        var index = el.data('index');
        var panel = this.scope.attr('relevant')[index];

        panel.attr('filter', data.item);
        panel.attr('value', true);
      },
      '.remove_filter click': function (el) {
        this.scope.attr('relevant').splice(el.data('index'), 1);
      },
      '{scope.relevant} change': function (list, item, which, type, val, old) {
        this.scope.attr('has_parent',
                        _.findWhere(this.scope.attr('relevant'),
                        {model_name: '__previous__'}));
        if (!/model_name/gi.test(which)) {
          return;
        }
        item.target.attr('filter', new can.Map());
        item.target.attr('value', false);
      }
    }
  });
})(window.can, window.can.$, window.GGRC);
<commit_msg>Audit field is now appeared in export panel filters menu<commit_after>/*!
    Copyright (C) 2016 Google Inc.
    Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
*/

(function (can, $, GGRC) {
  GGRC.Components('relevantFilter', {
    tag: 'relevant-filter',
    template: can.view(GGRC.mustache_path + '/mapper/relevant_filter.mustache'),
    scope: {
      define: {
        disableCreate: {
          type: 'boolean',
          'default': false
        }
      },
      relevant_menu_item: '@',
      show_all: '@',
      addFilter: function () {
        var menu = this.attr('menu');

        if (this.attr('relevant_menu_item') === 'parent' &&
             Number(this.attr('panel_number')) !== 0 &&
             !this.attr('has_parent')) {
          menu.unshift({
            title_singular: 'Previous objects',
            model_singular: '__previous__'
          });
        }

        this.attr('relevant').push({
          value: false,
          filter: new can.Map(),
          menu: menu,
          model_name: menu[0].model_singular
        });
      },
      menu: can.compute(function () {
        var type = this.attr('type');
        var mappings;
        var models;
        if (/true/i.test(this.attr('show_all'))) {
          // find all widget types and manually add Cycle since it's missing
          // convert names to CMS models and prune invalid (undefined)
          models = can.Map.keys(GGRC.tree_view.base_widgets_by_type);
          models = _.difference(_.unique(models),
                               ['CycleTaskEntry', 'CycleTaskGroupObject']);
          models = _.map(models, function (mapping) {
            return CMS.Models[mapping];
          });
          return _.sortBy(_.compact(models), 'model_singular');
        }

        if (type === 'AllObject') {
          type = GGRC.page_model.type;
        }
        mappings = GGRC.Mappings.get_canonical_mappings_for(type);
        return _.sortBy(_.compact(_.map(_.keys(mappings), function (mapping) {
          return CMS.Models[mapping];
        })), 'model_singular');
      })
    },
    events: {
      init: function () {
        this.setRelevant();
      },
      setRelevant: function () {
        this.scope.attr('relevant').replace([]);
        can.each(this.scope.attr('relevantTo') || [], function (item) {
          var model = CMS.Models[item.type].cache[item.id];
          this.scope.attr('relevant').push({
            value: true,
            filter: model,
            menu: this.scope.attr('menu'),
            model_name: model.constructor.shortName
          });
        }, this);
      },
      '.ui-autocomplete-input autocomplete:select': function (el, ev, data) {
        var index = el.data('index');
        var panel = this.scope.attr('relevant')[index];

        panel.attr('filter', data.item);
        panel.attr('value', true);
      },
      '.remove_filter click': function (el) {
        this.scope.attr('relevant').splice(el.data('index'), 1);
      },
      '{scope.relevant} change': function (list, item, which, type, val, old) {
        this.scope.attr('has_parent',
                        _.findWhere(this.scope.attr('relevant'),
                        {model_name: '__previous__'}));
        if (!/model_name/gi.test(which)) {
          return;
        }
        item.target.attr('filter', new can.Map());
        item.target.attr('value', false);
      }
    }
  });
})(window.can, window.can.$, window.GGRC);
<|endoftext|>


<commit_before>import QueryString    from './query_string';
import Utils          from './utils';
import $              from 'jquery';

function srcData(){
  var data = $('#srcData').html();
  var result = Utils.htmlDecodeWithRoot(data);
  if($(result).length == 1){ // We ended up with an empty <root> element. Try returning the raw result
    result = data;
  }
  result = result.trim();
  result = result.replace('<![CDATA[', '');
  result = result.replace('<!--[CDATA[', '');
  if(result.slice(-3) == ']]>'){
    result = result.slice(0,-3);
  }
  return result;
}

function bestValue(settings_prop, params_prop, default_prop){
  return window.OEA_SETTINGS[settings_prop] || QueryString.params()[params_prop] || default_prop;
}

export default {

  load(){

    var style = bestValue('style', 'style', null);
    if(style && style.indexOf('.css') < 0){
      style = '/assets/themes/' + style + '.css?body=1';
      $('head').append('<link href="' + style + '" media="all" rel="stylesheet">');
    }

    var settings = {
      apiUrl: bestValue('apiUrl', 'api_url', '/'),
      srcUrl: bestValue('srcUrl', 'src_url'),
      srcData: srcData,
      offline: bestValue('offline', 'offline', false),
      assessmentId: bestValue('assessmentId', 'assessment_id'),
      eId: bestValue('eId', 'eid'),
      externalUserId: bestValue('externalUserId', 'external_user_id'),
      keywords: bestValue('keywords', 'keywords'),
      resultsEndPoint: bestValue('resultsEndPoint', 'results_end_point', 'http://localhost:4200/api'),
      confidenceLevels: bestValue('confidenceLevels', 'confidence_levels', false),
      enableStart: bestValue('enableStart', 'enable_start', false),
      style: style
    };

    if(!settings.srcUrl && !settings.offline()){
      throw new Error("No src_url specified: specify a src_url in the url query params.");
    }

    return settings;
  }

}<commit_msg>Adding correct check to the oea_settings<commit_after>import QueryString    from './query_string';
import Utils          from './utils';
import $              from 'jquery';

function srcData(){
  var data = $('#srcData').html();
  var result = Utils.htmlDecodeWithRoot(data);
  if($(result).length == 1){ // We ended up with an empty <root> element. Try returning the raw result
    result = data;
  }
  result = result.trim();
  result = result.replace('<![CDATA[', '');
  result = result.replace('<!--[CDATA[', '');
  if(result.slice(-3) == ']]>'){
    result = result.slice(0,-3);
  }
  return result;
}

function bestValue(settings_prop, params_prop, default_prop){
	var globals = {};
	if(window.OEA_SETTINGS){
		globals = window.OEA_SETTINGS;
	}
  return globals[settings_prop] || QueryString.params()[params_prop] || default_prop;
}

export default {

  load(){

    var style = bestValue('style', 'style', null);
    if(style && style.indexOf('.css') < 0){
      style = '/assets/themes/' + style + '.css?body=1';
      $('head').append('<link href="' + style + '" media="all" rel="stylesheet">');
    }

    var settings = {
      apiUrl: bestValue('apiUrl', 'api_url', '/'),
      srcUrl: bestValue('srcUrl', 'src_url'),
      srcData: srcData,
      offline: bestValue('offline', 'offline', false),
      assessmentId: bestValue('assessmentId', 'assessment_id'),
      eId: bestValue('eId', 'eid'),
      externalUserId: bestValue('externalUserId', 'external_user_id'),
      keywords: bestValue('keywords', 'keywords'),
      resultsEndPoint: bestValue('resultsEndPoint', 'results_end_point', 'http://localhost:4200/api'),
      confidenceLevels: bestValue('confidenceLevels', 'confidence_levels', false),
      enableStart: bestValue('enableStart', 'enable_start', false),
      style: style
    };

    if(!settings.srcUrl && !settings.offline){
      throw new Error("No src_url specified: specify a src_url in the url query params.");
    }

    return settings;
  }

}<|endoftext|>


<commit_before>(function(env) {
    "use strict";

    function toDetail(img) {
        return img.replace(/tmb\.jpg$/, "det.jpg");
    }

    env.ddg_spice_in_theaters = function(api_result) {

        if (!api_result || api_result.error) {
            return Spice.failed('in_theaters');
        }

        Spice.add({
            id: 'in_theaters',
            name: 'Movies',
            data: api_result.movies,
	    signal: 'high',
            meta: {
                sourceName: 'Rotten Tomatoes',
                sourceUrl: 'http://www.rottentomatoes.com/movie/in-theaters/',
                total: api_result.movies,
                itemType: 'Movies'
            },
            normalize: function(item) {
		var position;
		if(item.ratings.critics_rating === "Fresh" || item.ratings.critics_rating === "Certified Fresh") {
		    position = "-256px -144px";
		} else if(item.ratings.critics_rating === "Rotten") {
		    position = "-272px -144px";
		}

                var image = toDetail(item.posters.detailed)
                return {
                    rating: item.ratings.critics_score >= 0 ? item.ratings.critics_score / 20 : 0,
                    image: image,
		    icon_url: DDG.get_asset_path('in_theaters','icons-v2.png'),
		    icon_image: position,
		    icon_class: position ? 'tomato--icon' : "",
		    abstract: Handlebars.helpers.ellipsis(item.synopsis, 200),
		    heading: item.title,
		    img_m: image,
		    url: item.links.alternate
                };
            },
            templates: {
		group: 'media',
		detail: 'products_item_detail',
                options: {
                    variant: 'poster',
		    subtitle_content: Spice.in_theaters.subtitle_content,
		    rating: false,
		    buy: Spice.in_theaters.buy
                }
            }
        });

        Spice.getDOM('in_theaters').find('.tile__body').addClass('is-hidden');
    }

    // Convert minutes to hr. min. format.
    // e.g. {{time 90}} will return 1 hr. 30 min.
    Handlebars.registerHelper("InTheaters_time", function(runtime) {
        var hours = '',
            minutes = runtime;

        if(runtime >= 60) {
            hours = Math.floor(runtime / 60) + ' hr. ';
            minutes = (runtime % 60);
        }

        return hours + (minutes > 0 ? minutes + ' min.' : '');
    });
}(this));
<commit_msg>Update in_theaters.js<commit_after>(function(env) {
    "use strict";

    // A change in the Rotten Tomatoes API returns images that end in _tmb.
    // This changes this to _det.
    function toDetail(img) {
        return img.replace(/tmb\.jpg$/, "det.jpg");
    }

    env.ddg_spice_in_theaters = function(api_result) {

        if (!api_result || api_result.error) {
            return Spice.failed('in_theaters');
        }

        Spice.add({
            id: 'in_theaters',
            name: 'Movies',
            data: api_result.movies,
	    signal: 'high',
            meta: {
                sourceName: 'Rotten Tomatoes',
                sourceUrl: 'http://www.rottentomatoes.com/movie/in-theaters/',
                total: api_result.movies,
                itemType: 'Movies'
            },
            normalize: function(item) {
		var position;
		if(item.ratings.critics_rating === "Fresh" || item.ratings.critics_rating === "Certified Fresh") {
		    position = "-256px -144px";
		} else if(item.ratings.critics_rating === "Rotten") {
		    position = "-272px -144px";
		}

                var image = toDetail(item.posters.detailed)
                return {
                    rating: item.ratings.critics_score >= 0 ? item.ratings.critics_score / 20 : 0,
                    image: image,
		    icon_url: DDG.get_asset_path('in_theaters','icons-v2.png'),
		    icon_image: position,
		    icon_class: position ? 'tomato--icon' : "",
		    abstract: Handlebars.helpers.ellipsis(item.synopsis, 200),
		    heading: item.title,
		    img_m: image,
		    url: item.links.alternate
                };
            },
            templates: {
		group: 'media',
		detail: 'products_item_detail',
                options: {
                    variant: 'poster',
		    subtitle_content: Spice.in_theaters.subtitle_content,
		    rating: false,
		    buy: Spice.in_theaters.buy
                }
            }
        });

        Spice.getDOM('in_theaters').find('.tile__body').addClass('is-hidden');
    }

    // Convert minutes to hr. min. format.
    // e.g. {{time 90}} will return 1 hr. 30 min.
    Handlebars.registerHelper("InTheaters_time", function(runtime) {
        var hours = '',
            minutes = runtime;

        if(runtime >= 60) {
            hours = Math.floor(runtime / 60) + ' hr. ';
            minutes = (runtime % 60);
        }

        return hours + (minutes > 0 ? minutes + ' min.' : '');
    });
}(this));
<|endoftext|>


<commit_before>/* Provides all functions necessary to deal with languages/translations
 * in the form. */

/* @public
 * Sets the languages available in this form. It updates the global lang
 * variable to which the create functions refer when they need to know
 * the languages. If there are existing fields it updates them by adding
 * or removing the locales.
 *
 * @param  array (or space-separated string) with two letter lang codes
 * @param  set to true, if this is an automated request. It will not
 *         warn about deletions or invalid data then. */
FormEditor.prototype.setLanguages = function(langs, automated) {
  // get languages from default text box unless given. It is assumed that
  // this is a user action, therefore warn if removing languages.
  var automated = automated || false;
  if(!$.isArray(langs))
    langs = $.trim(langs).split(/\s+/);

  // check input is valid
  var newLangs = [];
  for(var id in langs) {
    if(!langs[id].match(/^:?[a-z][a-z]$/)) {
      alert("Language code may only consist of two letters, optionally prepending a colon. E.g. :en, de. Given was: \""+langs[id]+"\"");
      return false;
    }
    newLangs.push(langs[id].length == 2 ? ":" + langs[id] : langs[id]);
  }

  // warn when removing langs
  var removedLangs = $(this.getLanguagesFromDom()).not(langs);
  if(!automated) {
    var rls = Array.prototype.join.call(removedLangs, ", ");
    var strng = "You are about to remove these language(s): "+rls+". Continue?";
    if(removedLangs.length > 0 && !confirm(strng))
      return false; // stop, because user doesn’t want to remove langs
  }

  if(!automated)
    this.addUndoStep("changing languages to: " + newLangs.join(", "));

  this.languages = newLangs;

  $("#availableLanguages").val(this.languages.join(" ").replace(/:/g, ""));

  // don't do removals/inserts on automated updates. These should only
  // occur once at the start and all fields should already sport the
  // correct languages.
  if(automated)
    return;

  // find translation groups
  $(".language").parent().each(function(ind, transGroup) {
    var path = $(transGroup).attr("id");
    var isTextArea = $(transGroup).find("textarea").length > 0;
    var l = newLangs.slice();
    $(transGroup).children(".language").each(function(ind, langGroup) {
      var lang = $(langGroup).children("span, label").html();
      var index = l.indexOf(lang);
      if(index >= 0) {
        l.splice(index, 1); // ack language is available in dom
      } else {
        $(langGroup).remove(); // remove superfluous lang
      }
    });
    // add missing languages to dom
    $.each(l, function(ind, lang) {
      $F().setPath($F().data, path + "/" + lang, isTextArea ? [] : "");
      $F().generatedHtml = "";
      if(isTextArea)
        $F().createLangTextArea(path, lang);
      else
        $F().createLangTextBox(path, lang);
      $(transGroup).append($F().generatedHtml);
    });
  });
}

/* @public
 * Opens a popup and asks the user which languages the form should
 * support. Considers the already existing ones and applies the user’s
 * changes if the dialog is not aborted. */
FormEditor.prototype.setLanguagesPopup = function() {
  var dat = prompt("Enter languages this form should support. Use two-letter lang codes and separate them by spaces.", $("#availableLanguages").val());
  if(!dat || dat == $("#availableLanguages").val()) return;
  this.setLanguages(dat);
};

/* @public
 * Retrieves languages currently available in the DOM /without/ looking
 * at the global variable. In other words, it find the languages which
 * are actually displayed.
 * @returns array of two-letter lang codes without leading colon */
FormEditor.prototype.getLanguagesFromDom = function() {
  // languages may either be defined in a heading (when genderized)
  // or a label. Collect all possible occurences and assert only valid
  // language codes have been gathered.
  var l = $(".language").children("span, label").map(function(ind, elm) {
    return $(elm).html();
  });
  l = $.unique(l);
  for(var i in l) {
    this.assert(l[i].match(/^:[a-z][a-z]$/)," Language Code must be in the :en format. Given lang: "+l[i]);
    l[i] = l[i].slice(1,3); // cut off colon
  }
  return l;
};

/* @public
 * Translates a given path. It therefore updates the data object from
 * the current DOM. This way the common functions to generate the text
 * boxes and action links may be used.
 * @param path    The path which should be translated. Will not object
 *                if the path is not suitable to be translated.
 * @param caller  The element which issues the call. Required if the
 *                un-translated text box should be replaced with the
 *                new version. Will replace the caller’s parent with
 *                the new textbox. Tailored for the default links. */
FormEditor.prototype.translatePath = function(path, caller) {
  this.addUndoStep("translating " + path);

  this.updateDataFromDom();

  var isTextArea = $(caller).parent().find("textarea").length > 0;

  // generate new object
  var oldText = "";
  try { // it may not exist, i.e. for empty boxes
    oldText = this.getPath(path);
  } catch(e) {}
  var translated = { };
  $.each(this.languages, function(i, lang) {
    translated[lang] = isTextArea ? oldText.split("\n") : oldText;
  });

  // inject new object
  this.setPath(this.data, path, translated);

  // update dom
  this.generatedHtml = "";
  if(isTextArea)
    this.createTranslateableTextArea(path);
  else
    this.createTranslateableTextBox(path);
  $(caller).parent().replaceWith(this.generatedHtml);
};

/* @public
 * Works just like translatePath, but the other way round. See there
 * for details. */
FormEditor.prototype.untranslatePath = function(path, caller) {
  this.addUndoStep("un-translating " + path);

  this.updateDataFromDom();

  // Try to get the English text first, if available. If it isn’t,
  // simply get the first string available.
  var oldText = "";
  try {
   oldText = this.getPath(path + "/:en");
  } catch(e) {
    try {
      oldText = this.getAnyElement(this.getPath(path));
      if(oldText == null) oldText = "";
    } catch(e) {}
  }

  var isTextArea = $(caller).parent().find("textarea").length > 0;

  // inject new object
  this.setPath(this.data, path, isTextArea ? oldText.split("\n") : oldText);
  this.generatedHtml = "";
  if(isTextArea)
    this.createTranslateableTextArea(path);
  else
    this.createTranslateableTextBox(path);
  $(caller).closest(".heading").replaceWith(this.generatedHtml);
};

/* @public
 * HTML Helper that creates a textbox which may be translated (or
 * already is). Determines all values by reading the from FromEditor’s
 * data variable. Appends to global HTML storage variable
 * (generatedHtml) */
FormEditor.prototype.createTranslateableTextBox = function(path) {
  var lang = [];
  var texts = this.getPath(path);

  if(typeof(texts) == "string") {
    this.openGroup();
    this.createTextBox(path, path.split("/").pop());
    this.createActionLink("$F().translatePath(\""+path+"\", this)", "Translate »");
    this.closeGroup();
  } else {
    this.createHeading(path);
    if(!this.translationsHaveGendering(texts))
      this.createActionLink("$F().untranslatePath(\""+path+"\", this)", "« Unify (no localization)");
    for(var lang in texts) {
      this.assert(lang.match(/^:[a-z][a-z]$/), "Language Code must be in the :en format. Given lang: "+lang);
      if(typeof(texts[lang] ) == "string") {
        this.createLangTextBox(path, lang);
      } else {
        this.createLangTextBoxGenderized(path, lang);
      }
    }
    this.closeHeading();
  }
};

/* @private
 * @returns true if at least one of the given translations has gendering */
FormEditor.prototype.translationsHaveGendering = function(texts) {
  for(var lang in texts) {
    if(typeof(texts[lang]) != "string")
      return true;
  }
  return false;
};
<commit_msg>be more intelligent about translating textboxes<commit_after>/* Provides all functions necessary to deal with languages/translations
 * in the form. */

/* @public
 * Sets the languages available in this form. It updates the global lang
 * variable to which the create functions refer when they need to know
 * the languages. If there are existing fields it updates them by adding
 * or removing the locales.
 *
 * @param  array (or space-separated string) with two letter lang codes
 * @param  set to true, if this is an automated request. It will not
 *         warn about deletions or invalid data then. */
FormEditor.prototype.setLanguages = function(langs, automated) {
  // get languages from default text box unless given. It is assumed that
  // this is a user action, therefore warn if removing languages.
  var automated = automated || false;
  if(!$.isArray(langs))
    langs = $.trim(langs).split(/\s+/);

  // check input is valid
  var newLangs = [];
  for(var id in langs) {
    if(!langs[id].match(/^:?[a-z][a-z]$/)) {
      alert("Language code may only consist of two letters, optionally prepending a colon. E.g. :en, de. Given was: \""+langs[id]+"\"");
      return false;
    }
    newLangs.push(langs[id].length == 2 ? ":" + langs[id] : langs[id]);
  }

  // warn when removing langs
  var removedLangs = $(this.getLanguagesFromDom()).not(langs);
  if(!automated) {
    var rls = Array.prototype.join.call(removedLangs, ", ");
    var strng = "You are about to remove these language(s): "+rls+". Continue?";
    if(removedLangs.length > 0 && !confirm(strng))
      return false; // stop, because user doesn’t want to remove langs
  }

  if(!automated)
    this.addUndoStep("changing languages to: " + newLangs.join(", "));

  this.languages = newLangs;

  $("#availableLanguages").val(this.languages.join(" ").replace(/:/g, ""));

  // don't do removals/inserts on automated updates. These should only
  // occur once at the start and all fields should already sport the
  // correct languages.
  if(automated)
    return;

  // find translation groups
  $(".language").parent().each(function(ind, transGroup) {
    var path = $(transGroup).attr("id");
    var isTextArea = $(transGroup).find("textarea").length > 0;
    var l = newLangs.slice();
    $(transGroup).children(".language").each(function(ind, langGroup) {
      var lang = $(langGroup).children("span, label").html();
      var index = l.indexOf(lang);
      if(index >= 0) {
        l.splice(index, 1); // ack language is available in dom
      } else {
        $(langGroup).remove(); // remove superfluous lang
      }
    });
    // add missing languages to dom
    $.each(l, function(ind, lang) {
      $F().setPath($F().data, path + "/" + lang, isTextArea ? [] : "");
      $F().generatedHtml = "";
      if(isTextArea)
        $F().createLangTextArea(path, lang);
      else
        $F().createLangTextBox(path, lang);
      $(transGroup).append($F().generatedHtml);
    });
  });
}

/* @public
 * Opens a popup and asks the user which languages the form should
 * support. Considers the already existing ones and applies the user’s
 * changes if the dialog is not aborted. */
FormEditor.prototype.setLanguagesPopup = function() {
  var dat = prompt("Enter languages this form should support. Use two-letter lang codes and separate them by spaces.", $("#availableLanguages").val());
  if(!dat || dat == $("#availableLanguages").val()) return;
  this.setLanguages(dat);
};

/* @public
 * Retrieves languages currently available in the DOM /without/ looking
 * at the global variable. In other words, it find the languages which
 * are actually displayed.
 * @returns array of two-letter lang codes without leading colon */
FormEditor.prototype.getLanguagesFromDom = function() {
  // languages may either be defined in a heading (when genderized)
  // or a label. Collect all possible occurences and assert only valid
  // language codes have been gathered.
  var l = $(".language").children("span, label").map(function(ind, elm) {
    return $(elm).html();
  });
  l = $.unique(l);
  for(var i in l) {
    this.assert(l[i].match(/^:[a-z][a-z]$/)," Language Code must be in the :en format. Given lang: "+l[i]);
    l[i] = l[i].slice(1,3); // cut off colon
  }
  return l;
};

/* @public
 * Translates a given path. It therefore updates the data object from
 * the current DOM. This way the common functions to generate the text
 * boxes and action links may be used.
 * @param path    The path which should be translated. Will not object
 *                if the path is not suitable to be translated.
 * @param caller  The element which issues the call. Required if the
 *                un-translated text box should be replaced with the
 *                new version. Will replace the caller’s parent with
 *                the new textbox. Tailored for the default links. */
FormEditor.prototype.translatePath = function(path, caller) {
  this.addUndoStep("translating " + path);

  this.updateDataFromDom();

  var isTextArea = $(caller).parent().find("textarea").length > 0;

  // generate new object
  var oldText = "";
  try { // it may not exist, i.e. for empty boxes
    oldText = this.getPath(path);
  } catch(e) {}
  var translated = { };
  $.each(this.languages, function(i, lang) {
    translated[lang] = isTextArea && !$.isArray(oldText) ? oldText.split("\n") : oldText;
  });

  // inject new object
  this.setPath(this.data, path, translated);

  // update dom
  this.generatedHtml = "";
  if(isTextArea)
    this.createTranslateableTextArea(path);
  else
    this.createTranslateableTextBox(path);
  $(caller).parent().replaceWith(this.generatedHtml);
};

/* @public
 * Works just like translatePath, but the other way round. See there
 * for details. */
FormEditor.prototype.untranslatePath = function(path, caller) {
  this.addUndoStep("un-translating " + path);

  this.updateDataFromDom();

  // Try to get the English text first, if available. If it isn’t,
  // simply get the first string available.
  var oldText = "";
  try {
   oldText = this.getPath(path + "/:en");
  } catch(e) {
    try {
      oldText = this.getAnyElement(this.getPath(path));
      if(oldText == null) oldText = "";
    } catch(e) {}
  }

  var isTextArea = $(caller).parent().find("textarea").length > 0;

  // inject new object
  this.setPath(this.data, path, isTextArea ? oldText.split("\n") : oldText);
  this.generatedHtml = "";
  if(isTextArea)
    this.createTranslateableTextArea(path);
  else
    this.createTranslateableTextBox(path);
  $(caller).closest(".heading").replaceWith(this.generatedHtml);
};

/* @public
 * HTML Helper that creates a textbox which may be translated (or
 * already is). Determines all values by reading the from FromEditor’s
 * data variable. Appends to global HTML storage variable
 * (generatedHtml) */
FormEditor.prototype.createTranslateableTextBox = function(path) {
  var lang = [];
  var texts = this.getPath(path);

  if(typeof(texts) == "string") {
    this.openGroup();
    this.createTextBox(path, path.split("/").pop());
    this.createActionLink("$F().translatePath(\""+path+"\", this)", "Translate »");
    this.closeGroup();
  } else {
    this.createHeading(path);
    if(!this.translationsHaveGendering(texts))
      this.createActionLink("$F().untranslatePath(\""+path+"\", this)", "« Unify (no localization)");
    for(var lang in texts) {
      this.assert(lang.match(/^:[a-z][a-z]$/), "Language Code must be in the :en format. Given lang: "+lang);
      if(typeof(texts[lang] ) == "string") {
        this.createLangTextBox(path, lang);
      } else {
        this.createLangTextBoxGenderized(path, lang);
      }
    }
    this.closeHeading();
  }
};

/* @private
 * @returns true if at least one of the given translations has gendering */
FormEditor.prototype.translationsHaveGendering = function(texts) {
  for(var lang in texts) {
    if(typeof(texts[lang]) != "string")
      return true;
  }
  return false;
};
<|endoftext|>


<commit_before>'use strict';

// Metabase Services
var MetabaseServices = angular.module('corvus.metabase.services', ['ngResource', 'ngCookies']);

MetabaseServices.factory('Metabase', ['$resource', '$cookies', function($resource, $cookies) {
    return $resource('/api/meta', {}, {
        db_form_input: {
            url:'/api/meta/db/form_input',
            method:'GET'
        },
        db_list: {
            url:'/api/meta/db/?org=:orgId',
            method:'GET',
            isArray:true
        },
        db_create: {
            url:'/api/meta/db/',
            method:'POST',
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}
        },
        validate_connection: {
            url:'/api/meta/db/validate/',
            method:'POST',
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }},
            transformRequest: function(data) {
		// API expects 'port' to be an int :imp:
		if (data.port) data.port = parseInt(data.port);

		return angular.toJson(data);
            }
        },
        db_get: {
            url:'/api/meta/db/:dbId',
            method:'GET',
            params:{dbId:'@dbId'}
        },
        db_update: {
            url:'/api/meta/db/:dbId',
            method:'PUT',
            params:{dbId:'@id'},
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}
        },
        db_delete: {
            url:'/api/meta/db/:dbId',
            method:'DELETE',
            params:{dbId:'@dbId'},
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }},
        },
        db_tables: {
            url:'/api/meta/db/:dbId/tables',
            method:'GET',
            params:{dbId:'@dbId'},
            isArray:true
        },
        db_idfields: {
            url:'/api/meta/db/:dbId/idfields',
            method:'GET',
            params:{dbId:'@dbId'},
            isArray:true
        },
        db_autocomplete_suggestions: {
            url:'/api/meta/db/:dbId/autocomplete_suggestions?prefix=:prefix',
            method:'GET',
            params:{dbId:'@dbId'},
            isArray:true
        },
        db_sync_metadata: {
            url:'/api/meta/db/:dbId/sync',
            method:'POST',
            params:{dbId:'@dbId'},
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}
        },
        table_list: {
            url:'/api/meta/table/',
            method:'GET',
            params:{tableId:'@tableId'},
            isArray:true
        },
        table_get: {
            url:'/api/meta/table/:tableId',
            method:'GET',
            params:{tableId:'@tableId'}
        },
        table_update: {
            url:'/api/meta/table/:tableId',
            method:'PUT',
            params:{tableId:'@id'},
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}
        },
        table_fields: {
            url:'/api/meta/table/:tableId/fields',
            method:'GET',
            params:{tableId:'@tableId'},
            isArray:true
        },
        table_fks: {
            url:'/api/meta/table/:tableId/fks',
            method:'GET',
            params:{tableId:'@tableId'},
            isArray:true
        },
        table_reorder_fields: {
            url:'/api/meta/table/:tableId/reorder',
            method:'POST',
            params:{tableId:'@tableId'},
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }},
        },
        table_segments: {
            url:'/api/meta/table/:tableId/segments',
            method:'GET',
            params:{tableId:'@tableId'},
            isArray:true
        },
        table_createsegment: {
            url:'/api/meta/table/:tableId/segments',
            method:'POST',
            params:{tableId:'@tableId'},
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}
        },
        table_dependents: {
            url:'/api/dependency/table/:tableId/dependents',
            method:'GET',
            params:{tableId:'@tableId'}
        },
        table_query_metadata: {
            url:'/api/meta/table/:tableId/query_metadata',
            method:'GET',
            params:{dbId:'@tableId'},
        },
        table_sync_metadata: {
            url:'/api/meta/table/:tableId/sync',
            method:'POST',
            params:{tableId:'@tableId'},
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}
        },
        field_get: {
            url:'/api/meta/field/:fieldId',
            method:'GET',
            params:{fieldId:'@fieldId'}
        },
        field_summary: {
            url:'/api/meta/field/:fieldId/summary',
            method:'GET',
            params:{fieldId:'@fieldId'},
            isArray:true
        },
        field_values: {
            url:'/api/meta/field/:fieldId/values',
            method:'GET',
            params:{fieldId:'@fieldId'},
        },
        field_value_map_update: {
            url:'/api/meta/field/:fieldId/value_map_update',
            method:'POST',
            params:{fieldId:'@fieldId'},
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}
        },
        field_update: {
            url:'/api/meta/field/:fieldId',
            method:'PUT',
            params:{fieldId:'@id'},
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}
        },
        field_pivots: {
            url:'/api/meta/field/:fieldId/pivots',
            method:'GET',
            params:{fieldId:'@fieldId'},
            isArray:true
        },
        field_foreignkeys: {
            url:'/api/meta/field/:fieldId/foreignkeys',
            method:'GET',
            params:{fieldId:'@fieldId'},
            isArray:true
        },
        field_addfk: {
            url:'/api/meta/field/:fieldId/foreignkeys',
            method:'POST',
            params:{fieldId:'@fieldId'},
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}
        },
        metadata: {
            url:'/api/meta/db/:dbId/metadata',
            method:'GET',
            params:{dbId:'@dbId'},
        },
        dataset: {
            url:'/api/meta/dataset',
            method:'POST',
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}
        }
    });
}]);

MetabaseServices.factory('ForeignKey', ['$resource', '$cookies', function($resource, $cookies) {
    return $resource('/api/meta/fk/:fkID', {}, {
        delete: {
            method:'DELETE',
            params:{fkID:'@fkID'},
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }},
        },

    });
}]);

MetabaseServices.factory('TableSegment', ['$resource', '$cookies', function($resource, $cookies) {
    return $resource('/api/meta/segment/:segmentID', {}, {
        delete: {
            method:'DELETE',
            params:{segmentID:'@segmentID'},
            headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }},
        },

    });
}]);
<commit_msg>remove some stray trailing commas in metabase.services.js :cry:<commit_after>'use strict';

// Metabase Services
var MetabaseServices = angular.module('corvus.metabase.services', ['ngResource', 'ngCookies']);

MetabaseServices.factory('Metabase', ['$resource', '$cookies', function($resource, $cookies) {
    return $resource('/api/meta', {}, {
        db_form_input: {
            url: '/api/meta/db/form_input',
            method: 'GET'
        },
        db_list: {
            url: '/api/meta/db/?org=:orgId',
            method: 'GET',
            isArray: true
        },
        db_create: {
            url: '/api/meta/db/',
            method: 'POST',
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            }
        },
        validate_connection: {
            url: '/api/meta/db/validate/',
            method: 'POST',
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            },
            transformRequest: function(data) {
                // API expects 'port' to be an int :imp:
                if (data.port) data.port = parseInt(data.port);
                return angular.toJson(data);
            }
        },
        db_get: {
            url: '/api/meta/db/:dbId',
            method: 'GET',
            params: {
                dbId: '@dbId'
            }
        },
        db_update: {
            url: '/api/meta/db/:dbId',
            method: 'PUT',
            params: {
                dbId: '@id'
            },
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            }
        },
        db_delete: {
            url: '/api/meta/db/:dbId',
            method: 'DELETE',
            params: {
                dbId: '@dbId'
            },
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            }
        },
        db_tables: {
            url: '/api/meta/db/:dbId/tables',
            method: 'GET',
            params: {
                dbId: '@dbId'
            },
            isArray: true
        },
        db_idfields: {
            url: '/api/meta/db/:dbId/idfields',
            method: 'GET',
            params: {
                dbId: '@dbId'
            },
            isArray: true
        },
        db_autocomplete_suggestions: {
            url: '/api/meta/db/:dbId/autocomplete_suggestions?prefix=:prefix',
            method: 'GET',
            params: {
                dbId: '@dbId'
            },
            isArray: true
        },
        db_sync_metadata: {
            url: '/api/meta/db/:dbId/sync',
            method: 'POST',
            params: {
                dbId: '@dbId'
            },
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            }
        },
        table_list: {
            url: '/api/meta/table/',
            method: 'GET',
            params: {
                tableId: '@tableId'
            },
            isArray: true
        },
        table_get: {
            url: '/api/meta/table/:tableId',
            method: 'GET',
            params: {
                tableId: '@tableId'
            }
        },
        table_update: {
            url: '/api/meta/table/:tableId',
            method: 'PUT',
            params: {
                tableId: '@id'
            },
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            }
        },
        table_fields: {
            url: '/api/meta/table/:tableId/fields',
            method: 'GET',
            params: {
                tableId: '@tableId'
            },
            isArray: true
        },
        table_fks: {
            url: '/api/meta/table/:tableId/fks',
            method: 'GET',
            params: {
                tableId: '@tableId'
            },
            isArray: true
        },
        table_reorder_fields: {
            url: '/api/meta/table/:tableId/reorder',
            method: 'POST',
            params: {
                tableId: '@tableId'
            },
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            },
        },
        table_segments: {
            url: '/api/meta/table/:tableId/segments',
            method: 'GET',
            params: {
                tableId: '@tableId'
            },
            isArray: true
        },
        table_createsegment: {
            url: '/api/meta/table/:tableId/segments',
            method: 'POST',
            params: {
                tableId: '@tableId'
            },
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            }
        },
        table_dependents: {
            url: '/api/dependency/table/:tableId/dependents',
            method: 'GET',
            params: {
                tableId: '@tableId'
            }
        },
        table_query_metadata: {
            url: '/api/meta/table/:tableId/query_metadata',
            method: 'GET',
            params: {
                dbId: '@tableId'
            }
        },
        table_sync_metadata: {
            url: '/api/meta/table/:tableId/sync',
            method: 'POST',
            params: {
                tableId: '@tableId'
            },
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            }
        },
        field_get: {
            url: '/api/meta/field/:fieldId',
            method: 'GET',
            params: {
                fieldId: '@fieldId'
            }
        },
        field_summary: {
            url: '/api/meta/field/:fieldId/summary',
            method: 'GET',
            params: {
                fieldId: '@fieldId'
            },
            isArray: true
        },
        field_values: {
            url: '/api/meta/field/:fieldId/values',
            method: 'GET',
            params: {
                fieldId: '@fieldId'
            }
        },
        field_value_map_update: {
            url: '/api/meta/field/:fieldId/value_map_update',
            method: 'POST',
            params: {
                fieldId: '@fieldId'
            },
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            }
        },
        field_update: {
            url: '/api/meta/field/:fieldId',
            method: 'PUT',
            params: {
                fieldId: '@id'
            },
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            }
        },
        field_pivots: {
            url: '/api/meta/field/:fieldId/pivots',
            method: 'GET',
            params: {
                fieldId: '@fieldId'
            },
            isArray: true
        },
        field_foreignkeys: {
            url: '/api/meta/field/:fieldId/foreignkeys',
            method: 'GET',
            params: {
                fieldId: '@fieldId'
            },
            isArray: true
        },
        field_addfk: {
            url: '/api/meta/field/:fieldId/foreignkeys',
            method: 'POST',
            params: {
                fieldId: '@fieldId'
            },
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            }
        },
        metadata: {
            url: '/api/meta/db/:dbId/metadata',
            method: 'GET',
            params: {
                dbId: '@dbId'
            }
        },
        dataset: {
            url: '/api/meta/dataset',
            method: 'POST',
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            }
        }
    });
}]);

MetabaseServices.factory('ForeignKey', ['$resource', '$cookies', function($resource, $cookies) {
    return $resource('/api/meta/fk/:fkID', {}, {
        delete: {
            method: 'DELETE',
            params: {
                fkID: '@fkID'
            },
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            },
        },

    });
}]);

MetabaseServices.factory('TableSegment', ['$resource', '$cookies', function($resource, $cookies) {
    return $resource('/api/meta/segment/:segmentID', {}, {
        delete: {
            method: 'DELETE',
            params: {
                segmentID: '@segmentID'
            },
            headers: {
                'X-CSRFToken': function() {
                    return $cookies.csrftoken;
                }
            },
        },

    });
}]);<|endoftext|>


<commit_before>/**
 * Constructs the LayoutManager instance.
 *
 * @class Contains methods for common layout schemes like putting images in rows or columns.
 *
 * @param {Seadragon} seadragon  Sets this.seadragon.
 */
Seadragon.LayoutManager = function LayoutManager(seadragon) {
    this.ensureArguments(arguments, 'LayoutManager');

    /**
     * Organizes DZIs into a given layout. TODO: currently doesn't work with uninitialized images.
     *
     * @param {boolean} [alingInRows=false]  If true, align in rows; otherwise in columns.
     * @param {number} [heightOrWidth=500]  If alignInRows: height of rows; otherwise width of columns.
     * @param {number} [spaceBetweenImages=0]
     * @param {number} [maxRowWidthOrColumnHeight=Infinity]  If not infinite, the next row/column is started
     *                                                       upon reaching the limit.
     * @param {boolean} [immediately=false]
     * @private
     */
    this._alignRowsOrColumns = function alignRowsOrColumns(alingInRows, heightOrWidth, spaceBetweenImages,
                                                           maxRowWidthOrColumnHeight, immediately) {
        var width, height, widthSum, heightSum, newBounds;
        var that = this;

        // Setting default values if parameters were not provided.
        heightOrWidth = heightOrWidth || 500; // it has to be >0
        spaceBetweenImages = spaceBetweenImages || 0;
        if (maxRowWidthOrColumnHeight == null) {
            maxRowWidthOrColumnHeight = Infinity;
        }

        if (this.controller.isLoading()) {
            setTimeout(function () {
                that._alignRowsOrColumns(alingInRows, heightOrWidth, spaceBetweenImages,
                    maxRowWidthOrColumnHeight, immediately);
            }, 100);
            return this;
        }

        widthSum = heightSum = 0;

        if (!maxRowWidthOrColumnHeight) {
            maxRowWidthOrColumnHeight = Infinity;
        }

        this.tiledImages.forEach(function (tiledImage) {
            // Compute the current state.
            if (alingInRows) {
                width = heightOrWidth * tiledImage.boundsSprings.getAspectRatio();
                height = heightOrWidth;
                if (widthSum + width > maxRowWidthOrColumnHeight) {
                    // Row width is now too much!
                    widthSum = 0;
                    heightSum += height + spaceBetweenImages;
                }
            }
            else { // Align in columns.
                width = heightOrWidth;
                height = heightOrWidth / tiledImage.boundsSprings.getAspectRatio();
                if (heightSum + height > maxRowWidthOrColumnHeight) {
                    // Column height is now too much!
                    heightSum = 0;
                    widthSum += width + spaceBetweenImages;
                }
            }

            // Set bounds.
            newBounds = new Seadragon.Rectangle(widthSum, heightSum, width, height);

            // Compute parameters after placing an image.
            if (alingInRows) {
                widthSum += width + spaceBetweenImages;
            } else {
                heightSum += height + spaceBetweenImages;
            }

            tiledImage.fitBounds(newBounds, immediately);
        });

        return this;
    };

    /**
     * Align images in rows. TODO: currently doesn't work with uninitialized images.
     *
     * @param {number} [height=500]  Height of a single row.
     * @param {number} [spaceBetweenImages=0] Space between images in a row and between columns.
     * @param {number} [maxRowWidth=Infinity]  Maximum row width. If the next image exceeded it, it's moved to
     *                                         the next row. If set to Infinity, only one row will
     *                                         be created.
     * @param {boolean} [immediately=false]
     */
    this.alignRows = function alignRows(height, spaceBetweenImages, maxRowWidth, immediately) {
        return this._alignRowsOrColumns(true, height, spaceBetweenImages, maxRowWidth, immediately);
    };

    /**
     * Align images in columns. TODO: currently doesn't work with uninitialized images.
     *
     * @see #alignRows
     *
     * @param {number} [width=500]
     * @param {number} [spaceBetweenImages=0]
     * @param {number} [maxColumnHeight=Infinity]
     * @param {boolean} [immediately=false]
     */
    this.alignColumns = function alignColumns(width, spaceBetweenImages, maxColumnHeight, immediately) {
        return this._alignRowsOrColumns(false, width, spaceBetweenImages, maxColumnHeight, immediately);
    };

    /**
     * Moves the viewport so that the given image is centered and zoomed as much as possible
     * while still being contained within the viewport.
     *
     * @param {number} whichImage  We fit the this.tiledImages[whichImage] image
     * @param {boolean} [current=false]
     * @param {boolean} [immediately=false]
     */
    this.fitImage = function fitImage(whichImage, current, immediately) {
        var tiledImage = this.tiledImages[whichImage];
        if (!tiledImage) {
            console.error('No image with number ' + whichImage);
            return this;
        }
        this.viewport.fitBounds(
            tiledImage.boundsSprings.getRectangle(current),
            immediately
        );
        return this;
    };

    function getFitImageFunction(whichImage) {
        return function () {
            this.layoutManager.fitImage(whichImage, false, true);
        };
    }

    /**
     * Hides currently visible images and shows only the given one.
     *
     * @param {number} whichImage  Index of the image to show.
     * @param {boolean} [immediately=false]
     * @param {boolean} [dontForceConstraints=false]
     */
    this.showOnlyImage = function showOnlyImage(whichImage, immediately, dontForceConstraints) {
        var tiledImage;

        this.showTiledImage(whichImage, immediately);
        if (this.tiledImages[whichImage] instanceof Seadragon.TiledImage) {
            getFitImageFunction(whichImage).call(this);
        } else {
            this.controller.tiledImagesCallbacks[whichImage].push(
                getFitImageFunction(whichImage)
            );
        }

        for (var i = 0; i < this.tiledImages.length; i++) {
            tiledImage = this.tiledImages[i];
            if (i !== whichImage) {
                if (tiledImage instanceof Seadragon.TiledImage) { // otherwise it's already hidden
                    this.hideTiledImage(i, immediately);
                }
            }
        }

        if (!dontForceConstraints) {
            this.controller.constrainToImage(whichImage);
        }

        return this;
    };

    /**
     * TODO document.
     *
     * @param {number} [height=500]
     * @param {boolean} [immediately=false]
     */
    this.alignCenterAndHeight = function alignCenterAndHeight(height, immediately) {
        var that = this;
        if (this.controller.isLoading()) {
            setTimeout(function () {
                that.alignCenterAndHeight(height, immediately);
            }, 100);
            return this;
        }

        height = height || 500; // it has to be >0

        function getFitBoundsFunction(bounds, immediately) {
            return function () {
                return this.fitBounds(bounds, immediately);
            };
        }

        for (var i = 0; i < this.tiledImages.length; i++) {
            var tiledImage = this.tiledImages[i];
            var newBounds = new Seadragon.Rectangle(0, 0, undefined, height);
            if (tiledImage instanceof Seadragon.TiledImage) {
                // Center in (0, 0), common height, width counted from height & aspect ratio.
                tiledImage.fitBounds(newBounds, immediately);
            } else {
                this.controller.tiledImagesCallbacks[i].push(
                    getFitBoundsFunction(newBounds, true) // we want deferred bounds modifications to invoke immediately
                );
            }
        }

        return this;
    };
};

Seadragon.LayoutManager.prototype = Object.create(seadragonProxy);
<commit_msg>a naming change<commit_after>/**
 * Constructs the LayoutManager instance.
 *
 * @class Contains methods for common layout schemes like putting images in rows or columns.
 *
 * @param {Seadragon} seadragon  Sets this.seadragon.
 */
Seadragon.LayoutManager = function LayoutManager(seadragon) {
    this.ensureArguments(arguments, 'LayoutManager');

    /**
     * Organizes DZIs into a given layout. TODO: currently doesn't work with uninitialized images.
     *
     * @param {boolean} [alingInRows=false]  If true, align in rows; otherwise in columns.
     * @param {number} [heightOrWidth=500]  If alignInRows: height of rows; otherwise width of columns.
     * @param {number} [spaceBetweenImages=0]
     * @param {number} [maxRowWidthOrColumnHeight=Infinity]  If not infinite, the next row/column is started
     *                                                       upon reaching the limit.
     * @param {boolean} [immediately=false]
     * @private
     */
    this._alignRowsOrColumns = function alignRowsOrColumns(alingInRows, heightOrWidth, spaceBetweenImages,
                                                           maxRowWidthOrColumnHeight, immediately) {
        var width, height, widthSum, heightSum, newBounds;
        var that = this;

        // Setting default values if parameters were not provided.
        heightOrWidth = heightOrWidth || 500; // it has to be >0
        spaceBetweenImages = spaceBetweenImages || 0;
        if (maxRowWidthOrColumnHeight == null) {
            maxRowWidthOrColumnHeight = Infinity;
        }

        if (this.controller.isLoading()) {
            setTimeout(function () {
                that._alignRowsOrColumns(alingInRows, heightOrWidth, spaceBetweenImages,
                    maxRowWidthOrColumnHeight, immediately);
            }, 100);
            return this;
        }

        widthSum = heightSum = 0;

        if (!maxRowWidthOrColumnHeight) {
            maxRowWidthOrColumnHeight = Infinity;
        }

        this.tiledImages.forEach(function (tiledImage) {
            // Compute the current state.
            if (alingInRows) {
                width = heightOrWidth * tiledImage.boundsSprings.getAspectRatio();
                height = heightOrWidth;
                if (widthSum + width > maxRowWidthOrColumnHeight) {
                    // Row width is now too much!
                    widthSum = 0;
                    heightSum += height + spaceBetweenImages;
                }
            }
            else { // Align in columns.
                width = heightOrWidth;
                height = heightOrWidth / tiledImage.boundsSprings.getAspectRatio();
                if (heightSum + height > maxRowWidthOrColumnHeight) {
                    // Column height is now too much!
                    heightSum = 0;
                    widthSum += width + spaceBetweenImages;
                }
            }

            // Set bounds.
            newBounds = new Seadragon.Rectangle(widthSum, heightSum, width, height);

            // Compute parameters after placing an image.
            if (alingInRows) {
                widthSum += width + spaceBetweenImages;
            } else {
                heightSum += height + spaceBetweenImages;
            }

            tiledImage.fitBounds(newBounds, immediately);
        });

        return this;
    };

    /**
     * Align images in rows. TODO: currently doesn't work with uninitialized images.
     *
     * @param {number} [height=500]  Height of a single row.
     * @param {number} [spaceBetweenImages=0] Space between images in a row and between columns.
     * @param {number} [maxRowWidth=Infinity]  Maximum row width. If the next image exceeded it, it's moved to
     *                                         the next row. If set to Infinity, only one row will
     *                                         be created.
     * @param {boolean} [immediately=false]
     */
    this.alignRows = function alignRows(height, spaceBetweenImages, maxRowWidth, immediately) {
        return this._alignRowsOrColumns(true, height, spaceBetweenImages, maxRowWidth, immediately);
    };

    /**
     * Align images in columns. TODO: currently doesn't work with uninitialized images.
     *
     * @see #alignRows
     *
     * @param {number} [width=500]
     * @param {number} [spaceBetweenImages=0]
     * @param {number} [maxColumnHeight=Infinity]
     * @param {boolean} [immediately=false]
     */
    this.alignColumns = function alignColumns(width, spaceBetweenImages, maxColumnHeight, immediately) {
        return this._alignRowsOrColumns(false, width, spaceBetweenImages, maxColumnHeight, immediately);
    };

    /**
     * Moves the viewport so that the given image is centered and zoomed as much as possible
     * while still being contained within the viewport.
     *
     * @param {number} whichImage  We fit the this.tiledImages[whichImage] image
     * @param {boolean} [current=false]
     * @param {boolean} [immediately=false]
     */
    this.fitImage = function fitImage(whichImage, current, immediately) {
        var tiledImage = this.tiledImages[whichImage];
        if (!tiledImage) {
            console.error('No image with number ' + whichImage);
            return this;
        }
        this.viewport.fitBounds(
            tiledImage.boundsSprings.getRectangle(current),
            immediately
        );
        return this;
    };

    function getFitImageFunction(whichImage) {
        return function () {
            this.layoutManager.fitImage(whichImage, false, true);
        };
    }

    /**
     * Hides currently visible images and shows only the given one.
     *
     * @param {number} whichImage  Index of the image to show.
     * @param {boolean} [immediately=false]
     * @param {boolean} [dontForceConstraints=false]
     */
    this.showOnlyImage = function showOnlyImage(whichImage, immediately, dontForceConstraints) {
        var tiledImage;

        this.showTiledImage(whichImage, immediately);
        if (this.tiledImages[whichImage] instanceof Seadragon.TiledImage) {
            getFitImageFunction(whichImage).call(this);
        } else {
            this.controller.tiledImagesCallbacks[whichImage].push(
                getFitImageFunction(whichImage)
            );
        }

        for (var i = 0; i < this.tiledImages.length; i++) {
            tiledImage = this.tiledImages[i];
            if (i !== whichImage) {
                if (tiledImage instanceof Seadragon.TiledImage) { // otherwise it's already hidden
                    this.hideTiledImage(i, immediately);
                }
            }
        }

        if (!dontForceConstraints) {
            this.controller.constrainToImage(whichImage);
        }

        return this;
    };

    /**
     * TODO document.
     *
     * @param {number} [height=500]
     * @param {boolean} [immediately=false]
     */
    this.alignCentersAndHeights = function alignCenterAndHeight(height, immediately) {
        var that = this;
        if (this.controller.isLoading()) {
            setTimeout(function () {
                that.alignCentersAndHeights(height, immediately);
            }, 100);
            return this;
        }

        height = height || 500; // it has to be >0

        function getFitBoundsFunction(bounds, immediately) {
            return function () {
                return this.fitBounds(bounds, immediately);
            };
        }

        for (var i = 0; i < this.tiledImages.length; i++) {
            var tiledImage = this.tiledImages[i];
            var newBounds = new Seadragon.Rectangle(0, 0, undefined, height);
            if (tiledImage instanceof Seadragon.TiledImage) {
                // Center in (0, 0), common height, width counted from height & aspect ratio.
                tiledImage.fitBounds(newBounds, immediately);
            } else {
                this.controller.tiledImagesCallbacks[i].push(
                    getFitBoundsFunction(newBounds, true) // we want deferred bounds modifications to invoke immediately
                );
            }
        }

        return this;
    };
};

Seadragon.LayoutManager.prototype = Object.create(seadragonProxy);
<|endoftext|>


<commit_before>var backbone = require('backbone');
var $ = require('jquery');
var _ = require('underscore');
var deep = require('deep-diff');

module.exports = {
  ListView: backbone.BaseListView.extend({
    events: {
      'change': function(event) {
        var id = this.$(this.options.container).val();

        $.getJSON(this.collection.get(id).get('schema'), (function(schemaData) {
          var
            // Keys of entered fields
            keys = _.keys(this.parent.getFilledValues());

          this.schemaData = schemaData;

          // If data can be lost - show confirmation message
          if(deep.diff(_.pick(schemaData.properties, keys), _.pick(this.parent.layout.form.schema.properties, keys)))
            return window.APP.layout.confirmationDialog
              .setMessage('Some data you have entered will be lost. Are you sure you want to change the Data Package Profile?')

              .setCallbacks({
                yes: (function() {
                  this.selectedValue = id;
                  this.parent.reset(schemaData);
                  window.APP.layout.confirmationDialog.deactivate();
                }).bind(this),

                no: (function() {
                  this.$(this.options.container).val(this.selectedValue);
                  window.APP.layout.confirmationDialog.deactivate();
                }).bind(this)
              })

              .activate();
          else
            this.selectedValue = id;

          this.parent.reset(schemaData);
        }).bind(this));
      }
    },

    // Returns schema object
    getSchema: function() { return this.schemaData; },

    // Returns schema URL
    getSelectedSchema: function() {
      return this.collection.get(this.$(this.options.container).val()).get('schema');
    },

    ItemView: backbone.BaseView.extend({
      render: function() {
        this.$el
          .attr('value', this.model.id)
          .html(this.model.get('title'));

        return this;
      },

      tagName: 'option'
    }),

    reset: function(collection) {
      backbone.BaseListView.prototype.reset.call(this, collection);
      this.setSelected(this.collection.at(0).get('id'));
      return this;
    },

    // Update selectbox and trigger change event
    setSelected: function(id) {
      this.$(this.options.container).val(id);

      // Used to restore previous value when user selects No in confirmation dialog
      this.selectedValue = id;

      return new Promise((function(RS, RJ) {
        $.getJSON(this.collection.get(id).get('schema'), (function(schemaData) {
          this.schemaData = schemaData;
          this.parent.reset(schemaData);
          RS(schemaData);
        }).bind(this));
      }).bind(this));
    },

    tagName: 'select'
  })
};
<commit_msg>Return false to prevent page reloading<commit_after>var backbone = require('backbone');
var $ = require('jquery');
var _ = require('underscore');
var deep = require('deep-diff');

module.exports = {
  ListView: backbone.BaseListView.extend({
    events: {
      'change': function(event) {
        var id = this.$(this.options.container).val();

        $.getJSON(this.collection.get(id).get('schema'), (function(schemaData) {
          var
            // Keys of entered fields
            keys = _.keys(this.parent.getFilledValues());

          this.schemaData = schemaData;

          // If data can be lost - show confirmation message
          if(deep.diff(_.pick(schemaData.properties, keys), _.pick(this.parent.layout.form.schema.properties, keys)))
            return window.APP.layout.confirmationDialog
              .setMessage('Some data you have entered will be lost. Are you sure you want to change the Data Package Profile?')

              .setCallbacks({
                yes: (function() {
                  this.selectedValue = id;
                  this.parent.reset(schemaData);
                  window.APP.layout.confirmationDialog.deactivate();
                  return false;
                }).bind(this),

                no: (function() {
                  this.$(this.options.container).val(this.selectedValue);
                  window.APP.layout.confirmationDialog.deactivate();
                  return false;
                }).bind(this)
              })

              .activate();
          else
            this.selectedValue = id;

          this.parent.reset(schemaData);
        }).bind(this));
      }
    },

    // Returns schema object
    getSchema: function() { return this.schemaData; },

    // Returns schema URL
    getSelectedSchema: function() {
      return this.collection.get(this.$(this.options.container).val()).get('schema');
    },

    ItemView: backbone.BaseView.extend({
      render: function() {
        this.$el
          .attr('value', this.model.id)
          .html(this.model.get('title'));

        return this;
      },

      tagName: 'option'
    }),

    reset: function(collection) {
      backbone.BaseListView.prototype.reset.call(this, collection);
      this.setSelected(this.collection.at(0).get('id'));
      return this;
    },

    // Update selectbox and trigger change event
    setSelected: function(id) {
      this.$(this.options.container).val(id);

      // Used to restore previous value when user selects No in confirmation dialog
      this.selectedValue = id;

      return new Promise((function(RS, RJ) {
        $.getJSON(this.collection.get(id).get('schema'), (function(schemaData) {
          this.schemaData = schemaData;
          this.parent.reset(schemaData);
          RS(schemaData);
        }).bind(this));
      }).bind(this));
    },

    tagName: 'select'
  })
};
<|endoftext|>