<commit_before>// ==UserScript==
// @name           futaba ID+IP popup
// @namespace      https://github.com/himuro-majika
// @description    同じIDやIPのレスをポップアップしちゃう
// @author         himuro_majika
// @include        http://*.2chan.net/*/res/*.htm
// @version        1.2
// @grant          GM_addStyle
// @run-at      document-idle
// @license        MIT
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAPUExURYv4i2PQYy2aLUe0R////zorx9oAAAAFdFJOU/////8A+7YOUwAAAElJREFUeNqUj1EOwDAIQoHn/c88bX+2fq0kRsAoUXVAfwzCttWsDWzw0kNVWd2tZ5K9gqmMZB8libt4pSg6YlO3RnTzyxePAAMAzqMDgTX8hYYAAAAASUVORK5CYII=
// ==/UserScript==
(function () {
	var USE_COUNTER = true;	// IDカウンターを表示する
	// USE_COUNTER = false;
	var USE_COUNTER_CURRENT = true;	// IDカウンターに現在の出現数を表示
	// USE_COUNTER_CURRENT = false;

	var Start = new Date().getTime();//count parsing time
	var saba = location.host.replace(".2chan.net","") +
		location.pathname.replace("futaba.htm","");
	var timer_show, timer_hide;
	var isIDIPThread = checkThreadMail();

	setClassAndNameThread();
	setClassAndNameRes();
	if (USE_COUNTER) {
		createCounter();
	}
	setEvent();
	setStyle();
	observeInserted();

	// ID表示・IP表示スレかどうか
	function checkThreadMail() {
		var mail = document.querySelector("html > body > form > font > b > a");
		if (mail && mail.href.match(/^mailto:i[dp]/i)) {
			return true;
		} else {
			return false;
		}
	}
	// ID/IPにclass,nameを設定する
	// 本文
	function setClassAndNameThread() {
		var form = document.querySelector(".thre") ?
			document.querySelector(".thre") :
			document.querySelector("html > body > form:not([enctype])");
		for (var i = 0; i < form.childNodes.length; i++) {
			var text = form.childNodes[i];
			if (text.tagName == "BLOCKQUOTE") {
				break;
			} else if (text.nodeValue) {
				var matchText = text.nodeValue.match(/(.+)(I[DP]:\S{8})(.*)/);
				if (matchText) {
					var date = document.createTextNode(matchText[1]);
					var id = matchText[2];
					var no = document.createTextNode(matchText[3]);
					var ida = document.createElement("a");
					ida.textContent = id;
					ida.setAttribute("class", "GM_fip_name GM_fip_name_thread");
					ida.setAttribute("name", id);
					text.parentNode.insertBefore(date, text);
					text.parentNode.insertBefore(ida, text);
					text.parentNode.insertBefore(no, text);
					text.parentNode.removeChild(text);
					break;
				}
			}
		}
	}
	// レス
	function setClassAndNameRes(node) {
		var atd = document.getElementsByClassName("rtd");
		if (arguments.length) {
			atd = node;
		}
		for (var i = 0; i < atd.length; i++) {
			var td = atd[i];
			for (var j = 0; j < td.childNodes.length; j++) {
				var text = td.childNodes[j];
				if (text.tagName == "BLOCKQUOTE") {
					break;
				} else if (text.nodeValue) {
					var matchText = text.nodeValue.match(/(.+)(I[DP]:\S{8})(.*)/);
					if (matchText) {
						var date = document.createTextNode(matchText[1]);
						var id = matchText[2];
						var no = document.createTextNode(matchText[3]);
						var ida = document.createElement("a");
						ida.textContent = id;
						ida.setAttribute("class", "GM_fip_name");
						ida.setAttribute("name", id);
						text.parentNode.insertBefore(date, text);
						text.parentNode.insertBefore(ida, text);
						text.parentNode.insertBefore(no, text);
						text.parentNode.removeChild(text);
						break;
					}
				}
			}
		}
	}
	// 出現数の表示
	function createCounter() {
		// return;
		var a = document.getElementsByClassName("GM_fip_name");
		var ids = {};
		for (var i = 0; i < a.length; i++) {
			var node = a[i];
			var id = node.name;
			if (USE_COUNTER_CURRENT) {
				if (ids[id]) {
					ids[id]++;
				} else {
					ids[id] = 1;
				}
			}
			// var name = document.querySelectorAll(".GM_fip_name[name='" + id + "']");
			var name = document.getElementsByName(id);
			var span;
			if (node.childNodes[1]) {
			// if (node.nextSibling.tagName == "SPAN") {
				// console.log(node.childNodes.length);
				// node.removeChild(node.childNodes[1]);
				span = node.childNodes[1];
				// span = node.nextSibling;
			} else {
				span = document.createElement("span");
				node.appendChild(span);
				// node.parentNode.insertBefore(span, node.nextSibling);
				span.setAttribute("class", "GM_fip_counter");
			}
			if (USE_COUNTER_CURRENT) {
				span.textContent = "[" + ids[id] + "/" + name.length + "]";
			} else {
				span.textContent = "[" + name.length + "]";
			}
		}
	}
	// イベントを設定
	function setEvent() {
		var aa = document.getElementsByClassName("GM_fip_name");
		for (var i = 0; i < aa.length; i++) {
			var a = aa[i];
			a.addEventListener("mouseover", show, true);
			a.addEventListener("mouseout", hide, true);
		}
	}
	// ポップアップを表示する
	function show(event) {
		clearTimeout(timer_show);
		delpop();
		var name = this.name;
		timer_show = setTimeout(function() {
			var wX;	//ポップアップ表示位置X
			var wY;	//ポップアップ表示位置Y
			var popup = document.createElement("div");
			popup.setAttribute("style", "visibility: hidden;");
			var divThread = document.createElement("div");
			var table = document.createElement("table");
			var tbody = document.createElement("tbody");
			popup.appendChild(divThread);
			popup.appendChild(table);
			table.appendChild(tbody);
			var tda = document.getElementsByName(name);
			for (var i = 0; i < tda.length; i++) {
				if (tda[i].classList.contains("GM_fip_name_thread")) {
					// スレ
					var form = tda[i].parentNode.cloneNode(true);
					for (var j = 0; j < form.childNodes.length; j++) {
						// 広告
						if (form.childNodes[j].className !== "tue") {
							divThread.appendChild(form.childNodes[j].cloneNode(true));
						}
						if (form.childNodes[j].tagName == "BLOCKQUOTE") {
							break;
						}
					}
				} else {
					// レス
					var tr = tda[i].parentNode.parentNode.cloneNode(true);
					tbody.appendChild(tr);
				}
			}
			var restable = [];
			popup.id = "GM_fip_pop";
			popup.setAttribute("class", "GM_fip_pop akahuku_reply_popup");
			popup.setAttribute("__akahuku_reply_popup_index", "");
			// popup.innerHTML = restable.join(" ");
			popup.addEventListener("mouseover",function() {
				clearTimeout(timer_hide);
			}, true);
			popup.addEventListener("mouseout",hide,true);
			var body = document.getElementsByTagName("body");
			body[0].appendChild(popup);
			wX = event.clientX + 10;
			wY = window.scrollY + event.clientY - popup.clientHeight - 10;
			if ( wY < 0 ) {	//ポップアップが上に見きれる時は下に表示
				wY = window.scrollY + event.clientY;
			}
			popup.setAttribute("style",
				"left:" + wX + "px; top:" + wY + "px; visibility: visible;");
		}, 100);
	}
	// ポップアップを消す
	function hide() {
		clearTimeout(timer_show);
		clearTimeout(timer_hide);
		timer_hide = setTimeout(delpop, 500);
	}

	function delpop() {
		clearTimeout(timer_hide);
		var doc_pop = document.getElementsByClassName("GM_fip_pop");
		if ( doc_pop ) {
			for (var i = 0; i < doc_pop.length; i++) {
				doc_pop[i].parentNode.removeChild(doc_pop[i]);
			}
		}
	}
	/*
	 * スタイル設定
	 */
	function setStyle() {
		var css = ".GM_fip_pop {" +
		"  position: absolute;" +
		"  z-index: 350;" +
		"  background-color: #eeaa88 !important;"+
		"}" +
		// ".GM_fip_pop > form {" +
		// "  background-color: #ffe" +
		// "}" +
		".GM_fip_pop > table {" +
		"  clear: both;" +
		"}" +
		".GM_fip_pop > div," +
		".GM_fip_pop > table > tbody > tr > td {" +
		"  color: #800000;" +
		"  font-size: 8pt !important;" +
		"}" +
		".GM_fip_pop > div," +
		".GM_fip_pop > table > tbody > tr > td > blockquote {" +
		"  margin-top: 0px !important;" +
		"  margin-bottom: 0px !important;" +
		"}" +
		".GM_fip_name {" +
		"  color: #F00;" +
		// "  font-family: monospace;" +
		"}" +
		".GM_fip_counter {" +
		"  margin: 0 0.3em;" +
		// "  color: #cc1105;" +
		"}";
		if (typeof GM_addStyle != "undefined") {
			GM_addStyle(css);
		} else if (typeof addStyle != "undefined") {
			addStyle(css);
		} else {
			var heads = document.getElementsByTagName("head");
			if (heads.length > 0) {
				var node = document.createElement("style");
				node.type = "text/css";
				node.appendChild(document.createTextNode(css));
				heads[0].appendChild(node);
			}
		}
	}
	// 続きを読むで追加されるレスを監視
	function observeInserted() {
		observeReloadStatus();
		var target = document.querySelector("html > body > form[action]:not([enctype])");
		var timer_reload;
		var observer = new MutationObserver(function(mutations) {
			mutations.forEach(function(mutation) {
				delpop();
				var nodes = mutation.addedNodes;
				for (var i = 0; i < nodes.length; i++) {
					// console.log(nodes[i]);
					if (
						nodes[i].tagName == "TABLE" &&
						nodes[i].id !== "akahuku_bottom_container"
					) {
						if (isIDIPThread) {
							setClassAndNameRes(nodes[i].childNodes[0].childNodes[0].childNodes);
						}
						clearTimeout(timer_reload);
						timer_reload = setTimeout(rel, 50);
					}
				}
			});
		});
		observer.observe(target, { childList: true });
		function rel() {
			if (!isIDIPThread) {
				setClassAndNameThread();
				setClassAndNameRes();
			}
			createCounter();
			setEvent();
		}
	}
	// 赤福のリロード状態を監視
	function observeReloadStatus() {
		var target = document.getElementById("akahuku_reload_status");
		if (!target) {
			return;
		}
		var observer = new MutationObserver(function(mutations) {
			mutations.forEach(function(mutation) {
				delpop();
			});
		});
		observer.observe(target, { childList: true });
	}
	console.log('Parsing '+saba+': '+((new Date()).getTime()-Start) +'msec');//log parsing time
})();
<commit_msg>Fix an issue that does not work on a res loaded by akahuku reloading caused by layout change.<commit_after>// ==UserScript==
// @name           futaba ID+IP popup
// @namespace      https://github.com/himuro-majika
// @description    同じIDやIPのレスをポップアップしちゃう
// @author         himuro_majika
// @include        http://*.2chan.net/*/res/*.htm
// @version        1.2
// @grant          GM_addStyle
// @run-at      document-idle
// @license        MIT
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAPUExURYv4i2PQYy2aLUe0R////zorx9oAAAAFdFJOU/////8A+7YOUwAAAElJREFUeNqUj1EOwDAIQoHn/c88bX+2fq0kRsAoUXVAfwzCttWsDWzw0kNVWd2tZ5K9gqmMZB8libt4pSg6YlO3RnTzyxePAAMAzqMDgTX8hYYAAAAASUVORK5CYII=
// ==/UserScript==
(function () {
	var USE_COUNTER = true;	// IDカウンターを表示する
	// USE_COUNTER = false;
	var USE_COUNTER_CURRENT = true;	// IDカウンターに現在の出現数を表示
	// USE_COUNTER_CURRENT = false;

	var Start = new Date().getTime();//count parsing time
	var saba = location.host.replace(".2chan.net","") +
		location.pathname.replace("futaba.htm","");
	var timer_show, timer_hide;
	var isIDIPThread = checkThreadMail();

	setClassAndNameThread();
	setClassAndNameRes();
	if (USE_COUNTER) {
		createCounter();
	}
	setEvent();
	setStyle();
	observeInserted();

	// ID表示・IP表示スレかどうか
	function checkThreadMail() {
		var mail = document.querySelector("html > body > form > font > b > a");
		if (mail && mail.href.match(/^mailto:i[dp]/i)) {
			return true;
		} else {
			return false;
		}
	}
	// ID/IPにclass,nameを設定する
	// 本文
	function setClassAndNameThread() {
		var form = document.querySelector(".thre") ?
			document.querySelector(".thre") :
			document.querySelector("html > body > form:not([enctype])");
		for (var i = 0; i < form.childNodes.length; i++) {
			var text = form.childNodes[i];
			if (text.tagName == "BLOCKQUOTE") {
				break;
			} else if (text.nodeValue) {
				var matchText = text.nodeValue.match(/(.+)(I[DP]:\S{8})(.*)/);
				if (matchText) {
					var date = document.createTextNode(matchText[1]);
					var id = matchText[2];
					var no = document.createTextNode(matchText[3]);
					var ida = document.createElement("a");
					ida.textContent = id;
					ida.setAttribute("class", "GM_fip_name GM_fip_name_thread");
					ida.setAttribute("name", id);
					text.parentNode.insertBefore(date, text);
					text.parentNode.insertBefore(ida, text);
					text.parentNode.insertBefore(no, text);
					text.parentNode.removeChild(text);
					break;
				}
			}
		}
	}
	// レス
	function setClassAndNameRes(node) {
		var atd = document.getElementsByClassName("rtd");
		if (arguments.length) {
			atd = node;
		}
		for (var i = 0; i < atd.length; i++) {
			var td = atd[i];
			for (var j = 0; j < td.childNodes.length; j++) {
				var text = td.childNodes[j];
				if (text.tagName == "BLOCKQUOTE") {
					break;
				} else if (text.nodeValue) {
					var matchText = text.nodeValue.match(/(.+)(I[DP]:\S{8})(.*)/);
					if (matchText) {
						var date = document.createTextNode(matchText[1]);
						var id = matchText[2];
						var no = document.createTextNode(matchText[3]);
						var ida = document.createElement("a");
						ida.textContent = id;
						ida.setAttribute("class", "GM_fip_name");
						ida.setAttribute("name", id);
						text.parentNode.insertBefore(date, text);
						text.parentNode.insertBefore(ida, text);
						text.parentNode.insertBefore(no, text);
						text.parentNode.removeChild(text);
						break;
					}
				}
			}
		}
	}
	// 出現数の表示
	function createCounter() {
		// return;
		var a = document.getElementsByClassName("GM_fip_name");
		var ids = {};
		for (var i = 0; i < a.length; i++) {
			var node = a[i];
			var id = node.name;
			if (USE_COUNTER_CURRENT) {
				if (ids[id]) {
					ids[id]++;
				} else {
					ids[id] = 1;
				}
			}
			// var name = document.querySelectorAll(".GM_fip_name[name='" + id + "']");
			var name = document.getElementsByName(id);
			var span;
			if (node.childNodes[1]) {
			// if (node.nextSibling.tagName == "SPAN") {
				// console.log(node.childNodes.length);
				// node.removeChild(node.childNodes[1]);
				span = node.childNodes[1];
				// span = node.nextSibling;
			} else {
				span = document.createElement("span");
				node.appendChild(span);
				// node.parentNode.insertBefore(span, node.nextSibling);
				span.setAttribute("class", "GM_fip_counter");
			}
			if (USE_COUNTER_CURRENT) {
				span.textContent = "[" + ids[id] + "/" + name.length + "]";
			} else {
				span.textContent = "[" + name.length + "]";
			}
		}
	}
	// イベントを設定
	function setEvent() {
		var aa = document.getElementsByClassName("GM_fip_name");
		for (var i = 0; i < aa.length; i++) {
			var a = aa[i];
			a.addEventListener("mouseover", show, true);
			a.addEventListener("mouseout", hide, true);
		}
	}
	// ポップアップを表示する
	function show(event) {
		clearTimeout(timer_show);
		delpop();
		var name = this.name;
		timer_show = setTimeout(function() {
			var wX;	//ポップアップ表示位置X
			var wY;	//ポップアップ表示位置Y
			var popup = document.createElement("div");
			popup.setAttribute("style", "visibility: hidden;");
			var divThread = document.createElement("div");
			var table = document.createElement("table");
			var tbody = document.createElement("tbody");
			popup.appendChild(divThread);
			popup.appendChild(table);
			table.appendChild(tbody);
			var tda = document.getElementsByName(name);
			for (var i = 0; i < tda.length; i++) {
				if (tda[i].classList.contains("GM_fip_name_thread")) {
					// スレ
					var form = tda[i].parentNode.cloneNode(true);
					for (var j = 0; j < form.childNodes.length; j++) {
						// 広告
						if (form.childNodes[j].className !== "tue") {
							divThread.appendChild(form.childNodes[j].cloneNode(true));
						}
						if (form.childNodes[j].tagName == "BLOCKQUOTE") {
							break;
						}
					}
				} else {
					// レス
					var tr = tda[i].parentNode.parentNode.cloneNode(true);
					tbody.appendChild(tr);
				}
			}
			var restable = [];
			popup.id = "GM_fip_pop";
			popup.setAttribute("class", "GM_fip_pop akahuku_reply_popup");
			popup.setAttribute("__akahuku_reply_popup_index", "");
			// popup.innerHTML = restable.join(" ");
			popup.addEventListener("mouseover",function() {
				clearTimeout(timer_hide);
			}, true);
			popup.addEventListener("mouseout",hide,true);
			var body = document.getElementsByTagName("body");
			body[0].appendChild(popup);
			wX = event.clientX + 10;
			wY = window.scrollY + event.clientY - popup.clientHeight - 10;
			if ( wY < 0 ) {	//ポップアップが上に見きれる時は下に表示
				wY = window.scrollY + event.clientY;
			}
			popup.setAttribute("style",
				"left:" + wX + "px; top:" + wY + "px; visibility: visible;");
		}, 100);
	}
	// ポップアップを消す
	function hide() {
		clearTimeout(timer_show);
		clearTimeout(timer_hide);
		timer_hide = setTimeout(delpop, 500);
	}

	function delpop() {
		clearTimeout(timer_hide);
		var doc_pop = document.getElementsByClassName("GM_fip_pop");
		if ( doc_pop ) {
			for (var i = 0; i < doc_pop.length; i++) {
				doc_pop[i].parentNode.removeChild(doc_pop[i]);
			}
		}
	}
	/*
	 * スタイル設定
	 */
	function setStyle() {
		var css = ".GM_fip_pop {" +
		"  position: absolute;" +
		"  z-index: 350;" +
		"  background-color: #eeaa88 !important;"+
		"}" +
		// ".GM_fip_pop > form {" +
		// "  background-color: #ffe" +
		// "}" +
		".GM_fip_pop > table {" +
		"  clear: both;" +
		"}" +
		".GM_fip_pop > div," +
		".GM_fip_pop > table > tbody > tr > td {" +
		"  color: #800000;" +
		"  font-size: 8pt !important;" +
		"}" +
		".GM_fip_pop > div," +
		".GM_fip_pop > table > tbody > tr > td > blockquote {" +
		"  margin-top: 0px !important;" +
		"  margin-bottom: 0px !important;" +
		"}" +
		".GM_fip_name {" +
		"  color: #F00;" +
		// "  font-family: monospace;" +
		"}" +
		".GM_fip_counter {" +
		"  margin: 0 0.3em;" +
		// "  color: #cc1105;" +
		"}";
		if (typeof GM_addStyle != "undefined") {
			GM_addStyle(css);
		} else if (typeof addStyle != "undefined") {
			addStyle(css);
		} else {
			var heads = document.getElementsByTagName("head");
			if (heads.length > 0) {
				var node = document.createElement("style");
				node.type = "text/css";
				node.appendChild(document.createTextNode(css));
				heads[0].appendChild(node);
			}
		}
	}
	// 続きを読むで追加されるレスを監視
	function observeInserted() {
		observeReloadStatus();
		var target = document.querySelector(".thre") ?
			document.querySelector(".thre") :
			document.querySelector("html > body > form[action]:not([enctype])");
		var timer_reload;
		var observer = new MutationObserver(function(mutations) {
			mutations.forEach(function(mutation) {
				delpop();
				var nodes = mutation.addedNodes;
				for (var i = 0; i < nodes.length; i++) {
					// console.log(nodes[i]);
					if (
						nodes[i].tagName == "TABLE" &&
						nodes[i].id !== "akahuku_bottom_container"
					) {
						if (isIDIPThread) {
							setClassAndNameRes(nodes[i].childNodes[0].childNodes[0].childNodes);
						}
						clearTimeout(timer_reload);
						timer_reload = setTimeout(rel, 50);
					}
				}
			});
		});
		observer.observe(target, { childList: true });
		function rel() {
			if (!isIDIPThread) {
				setClassAndNameThread();
				setClassAndNameRes();
			}
			createCounter();
			setEvent();
		}
	}
	// 赤福のリロード状態を監視
	function observeReloadStatus() {
		var target = document.getElementById("akahuku_reload_status");
		if (!target) {
			return;
		}
		var observer = new MutationObserver(function(mutations) {
			mutations.forEach(function(mutation) {
				delpop();
			});
		});
		observer.observe(target, { childList: true });
	}
	console.log('Parsing '+saba+': '+((new Date()).getTime()-Start) +'msec');//log parsing time
})();
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Novartis Institutes for BioMedical Research Inc.
 * 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.
 */
define(
  [
	'flight',
	'jquery',
	'component/query-table',
	'bootstrap'
  ],
  function (flight,$,queryTable) {
	'use strict';

	  return flight.component(appMgr);

	  function appMgr() {
		  $('nav.main-menu li').addClass('disabled');
	    this.getAppTemplate = function(app) {
	  	  var html = '
'+
          '
'+
            '<button id="app-qname-'+app+'" type="button" class="btn-qname btn btn-default pull-right">Queries</button>'+
            '
'+app+'
'+
          '
'+
          '
'+
            '
'+
              '<form>'+
                '
'+
                  '
'+
                    'Code'+
                    '<input type="text" id="app-code-'+app+'" class="form-control"/>'+
                  '
'+
                  '
'+
                    'Active'+
                    '<input type="checkbox" id="app-active-'+app+'" class="form-control app-active" value="1" data-toggle="tooltip" title="Will save automatically"/>'+
                  '
'+
                  '
'+
                    'Name'+
                    '<input type="text" id="app-name-'+app+'" class="form-control"/>'+
                  '
'+
                  '
'+
                    'Description'+
                    '<textarea id="app-desc-'+app+'" class="form-control"></textarea>'+
                  '
'+
                '
'+
                '
'+
                  '
'+
                    'Configuration'+
                    '<textarea id="app-conf-'+app+'" class="form-control" rows="11"></textarea>'+
                  '
'+
                '
'+
                '
'+
                  '
'+
                    '<button id="app-submit-'+app+'" class="app-save btn btn-primary pull-right">Save</button>'+
                  '
'+
                '
'+
              '</form>'+
            '
'+
          '
'+
        '
';
	  	  return html;
	  	};

	  	this.populate = function(e,d) {
        for(var i in d.data.RESULTSET.ROWS) {

          var appObj = d.data.RESULTSET.ROWS[i];
          if(!/BSRRSW/.test(appObj.APP))
          {
            var html = this.getAppTemplate(appObj.APP)
            this.select('accordion').append(html);
            if(appObj.ACTIVE == 0)
              $('#app-hdr-'+appObj.APP+' .panel-title').css('color','#777');
            if(appObj.CONF == "UNAUTHORIZED")
            {
              $('#app-submit-'+appObj.APP).remove();
              $('#app-'+appObj.APP).find('input,textarea').attr('disabled','disabled');
            }

            $('#app-'+appObj.APP).data('app-data',appObj);
          }
        }
        $(function () {
          $('[data-toggle="tooltip"]').tooltip();
        })
      };

      this.expressValues = function(e,d) {
        var panel  = $(e.target);
        var appObj = panel.data('app-data');
        var app;
        if(appObj !== undefined && appObj !== null)
        {
          app = appObj.APP;
          panel.find('#app-code-'+app).val(app);
          panel.find('#app-name-'+app).val(appObj.NAME);
          panel.find('#app-desc-'+app).val(appObj.DESCR);
          panel.find('#app-conf-'+app).val(appObj.CONF);
          if(appObj.ACTIVE == 1)
            panel.find('#app-active-'+app).attr('checked','checked');
          else
            panel.find('#app-active-'+app).removeAttr('checked');
        }

      };

	  	this.show = function(e,d) {
	  	  var self = this;
	  	  this.$node.removeClass('hidden');
	  	  this.select('accordion').empty().append(this.getAppTemplate('new'));
	  	  $('#app-hdr-new button').css('visibility','hidden');
	  	  $('#app-code-new').attr('placeholder','e.g., FOO');
	  	  $('#app-name-new').attr('placeholder','e.g., E-Scape, Smartopedia...');
	  	  $('#app-desc-new').attr('placeholder','Describe the data source here...');
	  	  $('#app-conf-new').val('#This is real example code.\n'+
	  	      '#see https://github.com/brettwooldridge/HikariCP for documentation\n'+
	  	      'jdbcUrl=e.g., jdbc:hsqldb:hsql://localhost/YADADB\n'+
	  	      'username=<replace>\n'+
	  	      'password=<replace>\n'+
	  	      'autoCommit=false\n'+
	  	      'connectionTimeout=300000\n'+
	  	      'idleTimeout=600000\n'+
	  	      'maxLifetime=1800000\n'+
	  	      'minimumIdle=5\n'+
	  	      'maximumPoolSize=100\n'+
	  	      'driverClassName=e.g., org.hsqldb.jdbc.JDBCDriver');
	  	  $.ajax({
          data: {
            q: self.attr.q_select_apps,
            s: 'a.app'
          },
          success: function(data) {
            self.trigger('populate-request.ya.app-mgr',{data:data})
          },
          error: function(d) {
            var msg = 'Session Timeout. Please login again.';
            var details = '';
            d['selector'] = '#app-mgr';
            if (d.responseJSON.Exception != undefined && d.responseJSON.StackTrace != undefined)
            {
              details = '
';
              if (d.responseJSON.Exception != undefined)
                details += "
Exception:" + d.responseJSON.Exception;
              if (d.responseJSON.StackTrace != undefined)
              {
                for (var i = 0; i < 5; i++)
                {
                  details += "
" + d.responseJSON.StackTrace[i];
                }
                details += "
...";
              }
              details += '
';
            }
            else if (typeof d == 'string')
            {
              msg += ' ' + d;
            }
            var html = '
';
            html += 'Uh Oh! ' + msg + '
';
            $(html).prependTo(d.selector);
            $(details).appendTo('.alert').hide();
          }
        });
	  	};

	  	this.saveApp = function(e,d) {
	  	  e.preventDefault();
	  	  var self   = this;
	  	  var q      = 'q_update_app';
	  	  var panel  = $(e.target).parents('.panel-collapse');
	  	  var appObj = panel.data('app-data');
	  	  var app, code, name, desc, conf, active;
	  	  if(appObj === undefined || appObj === null)
	  	  {
          app = 'new';
          code   = panel.find('#app-code-'+app).val();
          q   = 'q_insert_app';
	  	  }
	  	  else
	  	  {
	  	    app    = appObj.APP;
	  	    code   = app;
	  	  }
	  	  name   = panel.find('#app-name-'+app).val();
        desc   = panel.find('#app-desc-'+app).val();
        conf   = panel.find('#app-conf-'+app).val();
        active = panel.find('#app-active-'+app).is(':checked') ? 1 : 0;

        var progress = $('
'+
              '
'+
            '100% Complete'+
          '
'+
        '
');
        progress.insertBefore('#app-submit-'+app);
        var start = parseInt(new Date().getTime()/1000)
        setInterval(function() {
          var now = parseInt(new Date().getTime()/1000);
          $('.progress-bar').css('width',((now-start)/300)*100 + '%');
        },3000)

        var j = [{qname:self.attr[q],DATA:[{APP:code,NAME:name,DESCR:desc,CONF:conf,ACTIVE:active}]}];
	  	  if(app == 'new')
	  	    j.push({qname:self.attr.q_insert_admin,DATA:[{APP:code,USERID:$(self.attr.nest).data('userid')}]});

	  	  $.ajax({
	  	    type:'POST',
	  	    data:{
	  	      j : JSON.stringify(j)
	  	    },
	  	    success: function(resp) {
	  	      if(app == 'new')
	  	      {
	  	        var html = self.getAppTemplate(code);
	  	        var current, last;
	  	        var panels = $('#app-mgr .panel-title:gt(0)');
	  	        for(var i=0; i < panels.length; i++)
	  	        {
	  	          var current = $(panels[i]);
	  	          var next    = $(panels[i+1]);
	  	          var obj = {APP:code,NAME:name,DESCR:desc,CONF:conf,ACTIVE:active};
	  	          var $html = $(html)
	  	          if(current.text()  code)
	  	          {
	  	            $html.insertAfter(current.closest('.panel'));
	  	            $html.find('.panel-collapse').data('app-data',obj);
	  	            break;
	  	          }
	  	          else if(current.text() > code)
	  	          {
	  	            $html.insertBefore(current.closest('.panel'));
	  	            $html.find('.panel-collapse').data('app-data',obj);
                  break;
	  	          }
	  	        };
	  	        $('#app-new').collapse('hide');
	  	        panel.find('#app-code-'+app).val('');
	            panel.find('#app-name-'+app).val('');
	            panel.find('#app-desc-'+app).val('');
	            panel.find('#app-conf-'+app).val('');
	            panel.find('#app-active-'+app).removeAttr('checked');
	  	      }
	  	      progress.remove();
	  	      var alert = $('
Now we\'re cookin\'! Data saved successfully
');
	  	      alert.insertBefore('#app-submit-'+app);
	  	      setTimeout(function() { alert.fadeOut(); }, 1500);
	  	    },
	  	    error: function(xhr,status,error) {
	  	      progress.remove();
	  	      var alert = $('
Oh man! Data was not saved. Try again. (Status: '+xhr.status+', '+error+'.) This message will self-destruct in 30 seconds.
');
            alert.insertBefore('#app-submit-'+app);
            setTimeout(function() { alert.fadeOut(); }, 30000);
	  	    }
	  	  });
	  	};

	  	this.goToQueries = function(e,d) {
	  	  var app = $(e.target).closest('.panel').find('.panel-collapse').data('app-data').APP
	  	  $(this.attr['query-table']).data('app',app);
	  	  var self = this;
        this.$node.addClass('hidden');
        this.select('accordion').empty();
        queryTable.attachTo(document);
        this.trigger('view.ya.query-table',{app:app});
	  	};

	  	this.toggleActive = function(e,d) {
	  	  var self = this;
	  	  var $cbox = $(e.target);
	  	  var $panel = $cbox.closest('.panel');
        var appData =  $panel.find('.panel-collapse').data('app-data')
	  	  var app = !!appData ? appData.APP : $('#app-code-new').val();
        if(d.el.id != 'app-active-new')
        {
  	  	  if(!$cbox.is(':checked'))
  	  	  {
  	  	    var j = [{qname:self.attr.q_close_pool,DATA:[{APP:app}]}];
    	  	  $.ajax({
    	  	    type: 'POST',
    	  	    dataType: 'text',
    	  	    data: { j:JSON.stringify(j) },
    	  	    success: function(resp) {
    	  	      console.log(resp);
    	  	      $panel.find('.app-save').trigger('click');
    	  	    },
    	  	    error: function(xhr,status,error) {}
    	  	  });
  	  	  }
  	  	  else
  	  	  {
  	  	    $panel.find('.app-save').trigger('click');
  	  	  }
        }
	  	};

	  	this.defaultAttrs({
	  	  'q_select_apps' : 'YADA select apps',
	  	  'q_insert_app'  : 'YADA new app',
	  	  'q_update_app'  : 'YADA update app',
	  	  'q_delete_app'  : 'YADA delete app',
	  	  'q_insert_admin': 'YADA new app admin',
	  	  'q_close_pool'  : 'YADA close pool',
	  	  'accordion'     : '#app-accordion',
	  	  'save'          : '.app-save',
	  	  'btn-qname'     : '.btn-qname',
	  	  'query-table'   : '#query-table',
	  	  'nest'          : '.nest',
	  	  'app-active'    : 'input.app-active'
 	  	});

      this.after('initialize', function () {

      	this.on('init-request.ya.app-mgr',this.show);
      	this.on('populate-request.ya.app-mgr',this.populate);
      	this.on('shown.bs.collapse',this.expressValues);
      	this.on('click',{
      	  'save':this.saveApp,
      	  'btn-qname':this.goToQueries
      	});
      	this.on('change',{
      	  'app-active':this.toggleActive
      	})
      	// TODO remove the init-request trigger once login is finished
      	//this.trigger(this.$node,'init-request.ya.app-mgr',{});
      });
	  }
});
<commit_msg>default 'autoCommit' to false<commit_after>/*
 * Copyright 2016 Novartis Institutes for BioMedical Research Inc.
 * 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.
 */
define(
  [
	'flight',
	'jquery',
	'component/query-table',
	'bootstrap'
  ],
  function (flight,$,queryTable) {
	'use strict';

	  return flight.component(appMgr);

	  function appMgr() {
		  $('nav.main-menu li').addClass('disabled');
	    this.getAppTemplate = function(app) {
	  	  var html = '
'+
          '
'+
            '<button id="app-qname-'+app+'" type="button" class="btn-qname btn btn-default pull-right">Queries</button>'+
            '
'+app+'
'+
          '
'+
          '
'+
            '
'+
              '<form>'+
                '
'+
                  '
'+
                    'Code'+
                    '<input type="text" id="app-code-'+app+'" class="form-control"/>'+
                  '
'+
                  '
'+
                    'Active'+
                    '<input type="checkbox" id="app-active-'+app+'" class="form-control app-active" value="1" data-toggle="tooltip" title="Will save automatically"/>'+
                  '
'+
                  '
'+
                    'Name'+
                    '<input type="text" id="app-name-'+app+'" class="form-control"/>'+
                  '
'+
                  '
'+
                    'Description'+
                    '<textarea id="app-desc-'+app+'" class="form-control"></textarea>'+
                  '
'+
                '
'+
                '
'+
                  '
'+
                    'Configuration'+
                    '<textarea id="app-conf-'+app+'" class="form-control" rows="11"></textarea>'+
                  '
'+
                '
'+
                '
'+
                  '
'+
                    '<button id="app-submit-'+app+'" class="app-save btn btn-primary pull-right">Save</button>'+
                  '
'+
                '
'+
              '</form>'+
            '
'+
          '
'+
        '
';
	  	  return html;
	  	};

	  	this.populate = function(e,d) {
        for(var i in d.data.RESULTSET.ROWS) {

          var appObj = d.data.RESULTSET.ROWS[i];
          if(!/BSRRSW/.test(appObj.APP))
          {
            var html = this.getAppTemplate(appObj.APP)
            this.select('accordion').append(html);
            if(appObj.ACTIVE == 0)
              $('#app-hdr-'+appObj.APP+' .panel-title').css('color','#777');
            if(appObj.CONF == "UNAUTHORIZED")
            {
              $('#app-submit-'+appObj.APP).remove();
              $('#app-'+appObj.APP).find('input,textarea').attr('disabled','disabled');
            }

            $('#app-'+appObj.APP).data('app-data',appObj);
          }
        }
        $(function () {
          $('[data-toggle="tooltip"]').tooltip();
        })
      };

      this.expressValues = function(e,d) {
        var panel  = $(e.target);
        var appObj = panel.data('app-data');
        var app;
        if(appObj !== undefined && appObj !== null)
        {
          app = appObj.APP;
          panel.find('#app-code-'+app).val(app);
          panel.find('#app-name-'+app).val(appObj.NAME);
          panel.find('#app-desc-'+app).val(appObj.DESCR);
          panel.find('#app-conf-'+app).val(appObj.CONF);
          if(appObj.ACTIVE == 1)
            panel.find('#app-active-'+app).attr('checked','checked');
          else
            panel.find('#app-active-'+app).removeAttr('checked');
        }

      };

	  	this.show = function(e,d) {
	  	  var self = this;
	  	  this.$node.removeClass('hidden');
	  	  this.select('accordion').empty().append(this.getAppTemplate('new'));
	  	  $('#app-hdr-new button').css('visibility','hidden');
	  	  $('#app-code-new').attr('placeholder','e.g., FOO');
	  	  $('#app-name-new').attr('placeholder','e.g., E-Scape, Smartopedia...');
	  	  $('#app-desc-new').attr('placeholder','Describe the data source here...');
	  	  $('#app-conf-new').val('#This is real example code.\n'+
	  	      '#see https://github.com/brettwooldridge/HikariCP for documentation\n'+
	  	      'jdbcUrl=e.g., jdbc:hsqldb:hsql://localhost/YADADB\n'+
	  	      'username=<replace>\n'+
	  	      'password=<replace>\n'+
	  	      'connectionTimeout=300000\n'+
	  	      'idleTimeout=600000\n'+
	  	      'maxLifetime=1800000\n'+
	  	      'minimumIdle=5\n'+
	  	      'maximumPoolSize=100\n'+
	  	      'driverClassName=e.g., org.hsqldb.jdbc.JDBCDriver');
	  	  $.ajax({
          data: {
            q: self.attr.q_select_apps,
            s: 'a.app'
          },
          success: function(data) {
            self.trigger('populate-request.ya.app-mgr',{data:data})
          },
          error: function(d) {
            var msg = 'Session Timeout. Please login again.';
            var details = '';
            d['selector'] = '#app-mgr';
            if (d.responseJSON.Exception != undefined && d.responseJSON.StackTrace != undefined)
            {
              details = '
';
              if (d.responseJSON.Exception != undefined)
                details += "
Exception:" + d.responseJSON.Exception;
              if (d.responseJSON.StackTrace != undefined)
              {
                for (var i = 0; i < 5; i++)
                {
                  details += "
" + d.responseJSON.StackTrace[i];
                }
                details += "
...";
              }
              details += '
';
            }
            else if (typeof d == 'string')
            {
              msg += ' ' + d;
            }
            var html = '
';
            html += 'Uh Oh! ' + msg + '
';
            $(html).prependTo(d.selector);
            $(details).appendTo('.alert').hide();
          }
        });
	  	};

	  	this.saveApp = function(e,d) {
	  	  e.preventDefault();
	  	  var self   = this;
	  	  var q      = 'q_update_app';
	  	  var panel  = $(e.target).parents('.panel-collapse');
	  	  var appObj = panel.data('app-data');
	  	  var app, code, name, desc, conf, active;
	  	  if(appObj === undefined || appObj === null)
	  	  {
          app = 'new';
          code   = panel.find('#app-code-'+app).val();
          q   = 'q_insert_app';
	  	  }
	  	  else
	  	  {
	  	    app    = appObj.APP;
	  	    code   = app;
	  	  }
	  	  name   = panel.find('#app-name-'+app).val();
        desc   = panel.find('#app-desc-'+app).val();
        conf   = panel.find('#app-conf-'+app).val();
        active = panel.find('#app-active-'+app).is(':checked') ? 1 : 0;

        var progress = $('
'+
              '
'+
            '100% Complete'+
          '
'+
        '
');
        progress.insertBefore('#app-submit-'+app);
        var start = parseInt(new Date().getTime()/1000)
        setInterval(function() {
          var now = parseInt(new Date().getTime()/1000);
          $('.progress-bar').css('width',((now-start)/300)*100 + '%');
        },3000)

        var j = [{qname:self.attr[q],DATA:[{APP:code,NAME:name,DESCR:desc,CONF:conf,ACTIVE:active}]}];
	  	  if(app == 'new')
	  	    j.push({qname:self.attr.q_insert_admin,DATA:[{APP:code,USERID:$(self.attr.nest).data('userid')}]});

	  	  $.ajax({
	  	    type:'POST',
	  	    data:{
	  	      j : JSON.stringify(j)
	  	    },
	  	    success: function(resp) {
	  	      if(app == 'new')
	  	      {
	  	        var html = self.getAppTemplate(code);
	  	        var current, last;
	  	        var panels = $('#app-mgr .panel-title:gt(0)');
	  	        for(var i=0; i < panels.length; i++)
	  	        {
	  	          var current = $(panels[i]);
	  	          var next    = $(panels[i+1]);
	  	          var obj = {APP:code,NAME:name,DESCR:desc,CONF:conf,ACTIVE:active};
	  	          var $html = $(html)
	  	          if(current.text()  code)
	  	          {
	  	            $html.insertAfter(current.closest('.panel'));
	  	            $html.find('.panel-collapse').data('app-data',obj);
	  	            break;
	  	          }
	  	          else if(current.text() > code)
	  	          {
	  	            $html.insertBefore(current.closest('.panel'));
	  	            $html.find('.panel-collapse').data('app-data',obj);
                  break;
	  	          }
	  	        };
	  	        $('#app-new').collapse('hide');
	  	        panel.find('#app-code-'+app).val('');
	            panel.find('#app-name-'+app).val('');
	            panel.find('#app-desc-'+app).val('');
	            panel.find('#app-conf-'+app).val('');
	            panel.find('#app-active-'+app).removeAttr('checked');
	  	      }
	  	      progress.remove();
	  	      var alert = $('
Now we\'re cookin\'! Data saved successfully
');
	  	      alert.insertBefore('#app-submit-'+app);
	  	      setTimeout(function() { alert.fadeOut(); }, 1500);
	  	    },
	  	    error: function(xhr,status,error) {
	  	      progress.remove();
	  	      var alert = $('
Oh man! Data was not saved. Try again. (Status: '+xhr.status+', '+error+'.) This message will self-destruct in 30 seconds.
');
            alert.insertBefore('#app-submit-'+app);
            setTimeout(function() { alert.fadeOut(); }, 30000);
	  	    }
	  	  });
	  	};

	  	this.goToQueries = function(e,d) {
	  	  var app = $(e.target).closest('.panel').find('.panel-collapse').data('app-data').APP
	  	  $(this.attr['query-table']).data('app',app);
	  	  var self = this;
        this.$node.addClass('hidden');
        this.select('accordion').empty();
        queryTable.attachTo(document);
        this.trigger('view.ya.query-table',{app:app});
	  	};

	  	this.toggleActive = function(e,d) {
	  	  var self = this;
	  	  var $cbox = $(e.target);
	  	  var $panel = $cbox.closest('.panel');
        var appData =  $panel.find('.panel-collapse').data('app-data')
	  	  var app = !!appData ? appData.APP : $('#app-code-new').val();
        if(d.el.id != 'app-active-new')
        {
  	  	  if(!$cbox.is(':checked'))
  	  	  {
  	  	    var j = [{qname:self.attr.q_close_pool,DATA:[{APP:app}]}];
    	  	  $.ajax({
    	  	    type: 'POST',
    	  	    dataType: 'text',
    	  	    data: { j:JSON.stringify(j) },
    	  	    success: function(resp) {
    	  	      console.log(resp);
    	  	      $panel.find('.app-save').trigger('click');
    	  	    },
    	  	    error: function(xhr,status,error) {}
    	  	  });
  	  	  }
  	  	  else
  	  	  {
  	  	    $panel.find('.app-save').trigger('click');
  	  	  }
        }
	  	};

	  	this.defaultAttrs({
	  	  'q_select_apps' : 'YADA select apps',
	  	  'q_insert_app'  : 'YADA new app',
	  	  'q_update_app'  : 'YADA update app',
	  	  'q_delete_app'  : 'YADA delete app',
	  	  'q_insert_admin': 'YADA new app admin',
	  	  'q_close_pool'  : 'YADA close pool',
	  	  'accordion'     : '#app-accordion',
	  	  'save'          : '.app-save',
	  	  'btn-qname'     : '.btn-qname',
	  	  'query-table'   : '#query-table',
	  	  'nest'          : '.nest',
	  	  'app-active'    : 'input.app-active'
 	  	});

      this.after('initialize', function () {

      	this.on('init-request.ya.app-mgr',this.show);
      	this.on('populate-request.ya.app-mgr',this.populate);
      	this.on('shown.bs.collapse',this.expressValues);
      	this.on('click',{
      	  'save':this.saveApp,
      	  'btn-qname':this.goToQueries
      	});
      	this.on('change',{
      	  'app-active':this.toggleActive
      	})
      	// TODO remove the init-request trigger once login is finished
      	//this.trigger(this.$node,'init-request.ya.app-mgr',{});
      });
	  }
});
<|endoftext|>


<commit_before>/**
 * @ngdoc provider
 * @name filterWatcher
 * @kind function
 *
 * @description
 * store specific filters result in $$cache, based on scope life time(avoid memory leak).
 * on scope.$destroy remove it's cache from $$cache container
 */

angular.module('a8m.filter-watcher', [])
  .provider('filterWatcher', function() {

    this.$get = ['$window', '$rootScope', function($window, $rootScope) {

      /**
       * Cache storing
       * @type {Object}
       */
      var $$cache = {};

      /**
       * Scope listeners container
       * scope.$destroy => remove all cache keys
       * bind to current scope.
       * @type {Object}
       */
      var $$listeners = {};

      /**
       * $timeout without triggering the digest cycle
       * @type {function}
       */
      var $$timeout = $window.setTimeout;

      /**
       * @description
       * get `HashKey` string based on the given arguments.
       * @param fName
       * @param args
       * @returns {string}
       */
      function getHashKey(fName, args) {
        return [fName, angular.toJson(args)]
          .join('#')
          .replace(/"/g,'');
      }

      /**
       * @description
       * fir on $scope.$destroy,
       * remove cache based scope from `$$cache`,
       * and remove itself from `$$listeners`
       * @param event
       */
      function removeCache(event) {
        var id = event.targetScope.$id;
        forEach($$listeners[id], function(key) {
          delete $$cache[key];
        });
        delete $$listeners[id];
      }

      /**
       * @description
       * for angular version that greater than v.1.3.0
       * it clear cache when the digest cycle is end.
       */
      function cleanStateless() {
        $$timeout(function() {
          if(!$rootScope.$$phase)
            $$cache = {};
        }, 2000);
      }

      /**
       * @description
       * Store hashKeys in $$listeners container
       * on scope.$destroy, remove them all(bind an event).
       * @param scope
       * @param hashKey
       * @returns {*}
       */
      function addListener(scope, hashKey) {
        var id = scope.$id;
        if(isUndefined($$listeners[id])) {
          scope.$on('$destroy', removeCache);
          $$listeners[id] = [];
        }
        return $$listeners[id].push(hashKey);
      }

      /**
       * @description
       * return the `cacheKey` or undefined.
       * @param filterName
       * @param args
       * @returns {*}
       */
      function $$isMemoized(filterName, args) {
        var hashKey = getHashKey(filterName, args);
        return $$cache[hashKey];
      }

      /**
       * @description
       * store `result` in `$$cache` container, based on the hashKey.
       * add $destroy listener and return result
       * @param filterName
       * @param args
       * @param scope
       * @param result
       * @returns {*}
       */
      function $$memoize(filterName, args, scope, result) {
        var hashKey = getHashKey(filterName, args);
        //store result in `$$cache` container
        $$cache[hashKey] = result;
        // for angular versions that less than 1.3
        // add to `$destroy` listener, a cleaner callback
        if(isScope(scope)) {
          addListener(scope, hashKey);
        } else {
          cleanStateless();
        }
        return result;
      }

      return {
        isMemoized: $$isMemoized,
        memoize: $$memoize
      }

    }];
  });

<commit_msg>fix(watcher): hash-key convertor issue #119<commit_after>/**
 * @ngdoc provider
 * @name filterWatcher
 * @kind function
 *
 * @description
 * store specific filters result in $$cache, based on scope life time(avoid memory leak).
 * on scope.$destroy remove it's cache from $$cache container
 */

angular.module('a8m.filter-watcher', [])
  .provider('filterWatcher', function() {

    this.$get = ['$window', '$rootScope', function($window, $rootScope) {

      /**
       * Cache storing
       * @type {Object}
       */
      var $$cache = {};

      /**
       * Scope listeners container
       * scope.$destroy => remove all cache keys
       * bind to current scope.
       * @type {Object}
       */
      var $$listeners = {};

      /**
       * $timeout without triggering the digest cycle
       * @type {function}
       */
      var $$timeout = $window.setTimeout;

      /**
       * @description
       * get `HashKey` string based on the given arguments.
       * @param fName
       * @param args
       * @returns {string}
       */
      function getHashKey(fName, args) {
        function replacerFactory() {
          var cache = [];
          return function(key, val) {
            if(isObject(val) && !isNull(val)) {
              if (~cache.indexOf(val)) return '[Circular]';
              cache.push(val)
            }
            if($window == val) return '$WINDOW';
            if($window.document == val) return '$DOCUMENT';
            if(isScope(val)) return '$SCOPE';
            return val;
          }
        }
        return [fName, JSON.stringify(args, replacerFactory())]
          .join('#')
          .replace(/"/g,'');
      }

      /**
       * @description
       * fir on $scope.$destroy,
       * remove cache based scope from `$$cache`,
       * and remove itself from `$$listeners`
       * @param event
       */
      function removeCache(event) {
        var id = event.targetScope.$id;
        forEach($$listeners[id], function(key) {
          delete $$cache[key];
        });
        delete $$listeners[id];
      }

      /**
       * @description
       * for angular version that greater than v.1.3.0
       * it clear cache when the digest cycle is end.
       */
      function cleanStateless() {
        $$timeout(function() {
          if(!$rootScope.$$phase)
            $$cache = {};
        }, 2000);
      }

      /**
       * @description
       * Store hashKeys in $$listeners container
       * on scope.$destroy, remove them all(bind an event).
       * @param scope
       * @param hashKey
       * @returns {*}
       */
      function addListener(scope, hashKey) {
        var id = scope.$id;
        if(isUndefined($$listeners[id])) {
          scope.$on('$destroy', removeCache);
          $$listeners[id] = [];
        }
        return $$listeners[id].push(hashKey);
      }

      /**
       * @description
       * return the `cacheKey` or undefined.
       * @param filterName
       * @param args
       * @returns {*}
       */
      function $$isMemoized(filterName, args) {
        var hashKey = getHashKey(filterName, args);
        return $$cache[hashKey];
      }

      /**
       * @description
       * store `result` in `$$cache` container, based on the hashKey.
       * add $destroy listener and return result
       * @param filterName
       * @param args
       * @param scope
       * @param result
       * @returns {*}
       */
      function $$memoize(filterName, args, scope, result) {
        var hashKey = getHashKey(filterName, args);
        //store result in `$$cache` container
        $$cache[hashKey] = result;
        // for angular versions that less than 1.3
        // add to `$destroy` listener, a cleaner callback
        if(isScope(scope)) {
          addListener(scope, hashKey);
        } else {
          cleanStateless();
        }
        return result;
      }

      return {
        isMemoized: $$isMemoized,
        memoize: $$memoize
      }
    }];
  });

<|endoftext|>


<commit_before>'use strict'

const request = require('request'),
    Q = require('q'),
    companySearch = require('./api/company.js'),
    postSearch = require('./api/post.js'),
    preparePayload = require('./api/payloads-slack.js');

function isHandlerForRequest(context) {
  var responseUrl = context.userRequest.response_url || '';
  return (responseUrl && responseUrl.includes('hooks.slack.com')) ? true : false;
}

function getResponsePayload(context) {
  var textInput = context.userRequest.text;
  var payload = {
    // raw: {},
    data:  {},
    context: context
  }
  if (!textInput) {
      payload.data = preparePayload.inputPrompt();
      // payload.raw = payload.data;
      return Q.when(payload);
  }
  return Q.all([
      postSearch.findByText(textInput),
      companySearch.findByText(textInput)
  ])
  .then(getPayloadFromPostAndCompanySearch(postAndCompanySearchResults)
  //   function (postAndCompanySearchResults) {
  //     var posts = postAndCompanySearchResults[0];
  //     var companies = postAndCompanySearchResults[1];
  //     if (posts && posts.length) {
  //       // is it a bad idea to have a nested .then?
  //         return queryCompaniesOfPosts(posts)
  //           .then(function (posts){
  //               payload.data = preparePayload.posts(posts);
  //               // payload.raw = payload.data;
  //               return payload;
  //           });
  //     }
  //     else if (companies && companies.length) {
  //         payload.data = preparePayload.companies(companies);
  //         // payload.raw = payload.data;
  //         return payload;
  //     }
  //     else {
  //         payload.data = preparePayload.nothingFound();
  //         // payload.raw = payload.data;
  //         return payload;
  //     }
  // }
  )
}

// does it need to wrap up with 'res.status(200).end()' at end? Yes....
function sendResponseToPlatform(payload) {
  // shoot back an immediate Status 200 to let Slack know it's all cool
  payload.context.finishResponse();

  var deferred = Q.defer();
  var path = process.env.INCOMING_WEBHOOK_PATH;
  var uri = 'https://hooks.slack.com/services/' + path;

  payload.data.channel = payload.context.userRequest.channel_id;
  // console.log("Payload about to be sent back to Slack: " + JSON.stringify(payload.data));
  request({
    uri: uri,
    method: 'POST',
    body: JSON.stringify(payload.data)
  }, function (error, response, body) {
    if (error) {
      deferred.reject(error);
    }
    else {
      deferred.resolve();
    }
  });
  return deferred.promise;
}

function sendErrorResponse(err, context) {
  console.log("Ran into an error: " + err);
  var payload = {
    // raw: {},
    data: {},
    context: context
  };
  payload.data = preparePayload.error(err);
  sendResponseToPlatform(errorPayload);
}

//  ---------- Helper Methods ----------------

function getPayloadFromPostAndCompanySearch(postAndCompanySearchResults) {
  var posts = postAndCompanySearchResults[0];
  var companies = postAndCompanySearchResults[1];
  if (posts && posts.length) {
    // is it a bad idea to have a nested .then?
      return queryCompaniesOfPosts(posts)
        .then(function (posts){
            payload.data = preparePayload.posts(posts);
            // payload.raw = payload.data;
            return payload;
        });
  }
  else if (companies && companies.length) {
      payload.data = preparePayload.companies(companies);
      // payload.raw = payload.data;
      return payload;
  }
  else {
      payload.data = preparePayload.nothingFound();
      // payload.raw = payload.data;
      return payload;
  }
}

function queryCompaniesOfPosts(posts) {
    // console.log("About to attach Companies to Posts.");
    var companyIDs = [];
    for (let i = 0; i < posts.length; i++) {
        companyIDs.push(posts[i].companyId);
    };
    return Q.when(companySearch.findByIds(companyIDs))
      // .then(attachCompaniesToPosts(companies))
      .then(function (companies) {
        var companyTable = {};
        for (let i = 0; i < companies.length; i++) {
            companyTable[companies[i]._id] = companies[i];
        };
        for (let i = 0; i < posts.length; i++) {
            let cID = posts[i].companyId;
            posts[i].company = companyTable[cID];
        };
        return posts;
    })
}

// will this carry the reference to the Posts object?
// if not, how to pass in Posts?
// function attachCompaniesToPosts(companies) {
//   var companyTable = {};
//   for (let i = 0; i < companies.length; i++) {
//       companyTable[companies[i]._id] = companies[i];
//   };
//   for (let i = 0; i < posts.length; i++) {
//       let cID = posts[i].companyId;
//       posts[i].company = companyTable[cID];
//   };
//   return posts;
// }

// ------------------------------

module.exports = {
  isHandlerForRequest: isHandlerForRequest,
  getResponsePayload: getResponsePayload,
  sendResponseToPlatform: sendResponseToPlatform,
  sendErrorResponse: sendErrorResponse
}
<commit_msg>Debug<commit_after>'use strict'

const request = require('request'),
    Q = require('q'),
    companySearch = require('./api/company.js'),
    postSearch = require('./api/post.js'),
    preparePayload = require('./api/payloads-slack.js');

function isHandlerForRequest(context) {
  var responseUrl = context.userRequest.response_url || '';
  return (responseUrl && responseUrl.includes('hooks.slack.com')) ? true : false;
}

function getResponsePayload(context) {
  var textInput = context.userRequest.text;
  var payload = {
    // raw: {},
    data:  {},
    context: context
  }
  if (!textInput) {
      payload.data = preparePayload.inputPrompt();
      // payload.raw = payload.data;
      return Q.when(payload);
  }
  return Q.all([
      postSearch.findByText(textInput),
      companySearch.findByText(textInput)
  ])
  .then(
    // getPayloadFromPostAndCompanySearch(postAndCompanySearchResults)
    function (postAndCompanySearchResults) {
      var posts = postAndCompanySearchResults[0];
      var companies = postAndCompanySearchResults[1];
      if (posts && posts.length) {
        // is it a bad idea to have a nested .then?
          return queryCompaniesOfPosts(posts)
            .then(function (posts){
                payload.data = preparePayload.posts(posts);
                // payload.raw = payload.data;
                return payload;
            });
      }
      else if (companies && companies.length) {
          payload.data = preparePayload.companies(companies);
          // payload.raw = payload.data;
          return payload;
      }
      else {
          payload.data = preparePayload.nothingFound();
          // payload.raw = payload.data;
          return payload;
      }
  }
  )
}

// does it need to wrap up with 'res.status(200).end()' at end? Yes....
function sendResponseToPlatform(payload) {
  // shoot back an immediate Status 200 to let Slack know it's all cool
  payload.context.finishResponse();

  var deferred = Q.defer();
  var path = process.env.INCOMING_WEBHOOK_PATH;
  var uri = 'https://hooks.slack.com/services/' + path;

  payload.data.channel = payload.context.userRequest.channel_id;
  // console.log("Payload about to be sent back to Slack: " + JSON.stringify(payload.data));
  request({
    uri: uri,
    method: 'POST',
    body: JSON.stringify(payload.data)
  }, function (error, response, body) {
    if (error) {
      deferred.reject(error);
    }
    else {
      deferred.resolve();
    }
  });
  return deferred.promise;
}

function sendErrorResponse(err, context) {
  console.log("Ran into an error: " + err);
  var payload = {
    // raw: {},
    data: {},
    context: context
  };
  payload.data = preparePayload.error(err);
  sendResponseToPlatform(errorPayload);
}

//  ---------- Helper Methods ----------------

// function getPayloadFromPostAndCompanySearch(postAndCompanySearchResults) {
//   var posts = postAndCompanySearchResults[0];
//   var companies = postAndCompanySearchResults[1];
//   if (posts && posts.length) {
//     // is it a bad idea to have a nested .then?
//       return queryCompaniesOfPosts(posts)
//         .then(function (posts){
//             payload.data = preparePayload.posts(posts);
//             // payload.raw = payload.data;
//             return payload;
//         });
//   }
//   else if (companies && companies.length) {
//       payload.data = preparePayload.companies(companies);
//       // payload.raw = payload.data;
//       return payload;
//   }
//   else {
//       payload.data = preparePayload.nothingFound();
//       // payload.raw = payload.data;
//       return payload;
//   }
// }

function queryCompaniesOfPosts(posts) {
    // console.log("About to attach Companies to Posts.");
    var companyIDs = [];
    for (let i = 0; i < posts.length; i++) {
        companyIDs.push(posts[i].companyId);
    };
    return Q.when(companySearch.findByIds(companyIDs))
      // .then(attachCompaniesToPosts(companies))
      .then(function (companies) {
        var companyTable = {};
        for (let i = 0; i < companies.length; i++) {
            companyTable[companies[i]._id] = companies[i];
        };
        for (let i = 0; i < posts.length; i++) {
            let cID = posts[i].companyId;
            posts[i].company = companyTable[cID];
        };
        return posts;
    })
}

// will this carry the reference to the Posts object?
// if not, how to pass in Posts?
// function attachCompaniesToPosts(companies) {
//   var companyTable = {};
//   for (let i = 0; i < companies.length; i++) {
//       companyTable[companies[i]._id] = companies[i];
//   };
//   for (let i = 0; i < posts.length; i++) {
//       let cID = posts[i].companyId;
//       posts[i].company = companyTable[cID];
//   };
//   return posts;
// }

// ------------------------------

module.exports = {
  isHandlerForRequest: isHandlerForRequest,
  getResponsePayload: getResponsePayload,
  sendResponseToPlatform: sendResponseToPlatform,
  sendErrorResponse: sendErrorResponse
}
<|endoftext|>


<commit_before>describe("About Functions", function() {

  it("should declare functions", function() {

    function add(a, b) {
      return a + b;
    }

    expect(add(1, 2)).toBe(FILL_ME_IN);
  });

  it("should know internal variables override outer variables", function () {
    var message = "Outer";

    function getMessage() {
      return message;
    }

    function overrideMessage() {
      var message = "Inner";
      return message;
    }

    expect(getMessage()).toBe(FILL_ME_IN);
    expect(overrideMessage()).toBe(FILL_ME_IN);
    expect(message).toBe(FILL_ME_IN);
  });

  it("should have lexical scoping", function () {
    var variable = "top-level";
    function parentfunction() {
        var variable = "local";
      function childfunction() {
          return variable;
      }
      return childfunction();
    }
    expect(parentfunction()).toBe(FILL_ME_IN);
  });

  it("should use lexical scoping to synthesise functions", function () {

    function makeIncreaseByFunction(increaseByAmount) {
      return function (numberToIncrease) {
        return numberToIncrease + increaseByAmount;
      };
    }

    var increaseBy3 = makeIncreaseByFunction(3);
    var increaseBy5 = makeIncreaseByFunction(5);

    expect(increaseBy3(10) + increaseBy5(10)).toBe(FILL_ME_IN);
  });

  it("should allow extra function arguments", function () {

    function returnFirstArg(firstArg) {
      return firstArg;
    }

    expect(returnFirstArg("first", "second", "third")).toBe(FILL_ME_IN);

    function returnSecondArg(firstArg, secondArg) {
      return secondArg;
    }

    expect(returnSecondArg("only give first arg")).toBe(FILL_ME_IN);

    function returnAllArgs() {
      var argsArray = [];
      for (var i = 0; i < arguments.length; i += 1) {
        argsArray.push(arguments[i]);
      }
      return argsArray.join(",");
    }

    expect(returnAllArgs("first", "second", "third")).toBe(FILL_ME_IN);
  });

  it("should pass functions as values", function () {

    var appendRules = function (name) {
      return name + " rules!";
    };

    var appendDoubleRules = function (name) {
      return name + " totally rules!";
    };

    var praiseSinger = { givePraise: appendRules };
    expect(praiseSinger.givePraise("John")).toBe(FILL_ME_IN);

    praiseSinger.givePraise = appendDoubleRules;
    expect(praiseSinger.givePraise("Mary")).toBe(FILL_ME_IN);

  });

  it("should use function body as a string", function () {
    var add = new Function("a", "b", "return a + b;");
    expect(add(1, 2)).toBe(FILL_ME_IN);

    var multiply = function (a, b) {
      //An internal comment
      return a * b;
    };
    expect(multiply.toString()).toBe(FILL_ME_IN);
  });
});
<commit_msg>Completed AboutFunctions koan<commit_after>describe("About Functions", function() {

  it("should declare functions", function() {

    function add(a, b) {
      return a + b;
    }

    expect(add(1, 2)).toBe(3);
  });

  it("should know internal variables override outer variables", function () {
    var message = "Outer";

    function getMessage() {
      return message;
    }

    function overrideMessage() {
      var message = "Inner";
      return message;
    }

    expect(getMessage()).toBe("Outer");
    expect(overrideMessage()).toBe("Inner");
    expect(message).toBe("Outer");
  });

  it("should have lexical scoping", function () {
    var variable = "top-level";
    function parentfunction() {
        var variable = "local";
      function childfunction() {
          return variable;
      }
      return childfunction();
    }
    expect(parentfunction()).toBe("local");
  });

  it("should use lexical scoping to synthesise functions", function () {

    function makeIncreaseByFunction(increaseByAmount) {
      return function (numberToIncrease) {
        return numberToIncrease + increaseByAmount;
      };
    }

    var increaseBy3 = makeIncreaseByFunction(3);
    var increaseBy5 = makeIncreaseByFunction(5);

    expect(increaseBy3(10) + increaseBy5(10)).toBe(28);
  });

  it("should allow extra function arguments", function () {

    function returnFirstArg(firstArg) {
      return firstArg;
    }

    expect(returnFirstArg("first", "second", "third")).toBe("first");

    function returnSecondArg(firstArg, secondArg) {
      return secondArg;
    }

    expect(returnSecondArg("only give first arg")).toBe(undefined);

    function returnAllArgs() {
      var argsArray = [];
      for (var i = 0; i < arguments.length; i += 1) {
        argsArray.push(arguments[i]);
      }
      return argsArray.join(",");
    }

    expect(returnAllArgs("first", "second", "third")).toBe("first,second,third");
  });

  it("should pass functions as values", function () {

    var appendRules = function (name) {
      return name + " rules!";
    };

    var appendDoubleRules = function (name) {
      return name + " totally rules!";
    };

    var praiseSinger = { givePraise: appendRules };
    expect(praiseSinger.givePraise("John")).toBe("John rules!");

    praiseSinger.givePraise = appendDoubleRules;
    expect(praiseSinger.givePraise("Mary")).toBe("Mary totally rules!");

  });

  it("should use function body as a string", function () {
    var add = new Function("a", "b", "return a + b;");
    expect(add(1, 2)).toBe(3);

    var multiply = function (a, b) {
      //An internal comment
      return a * b;
    };
    expect(multiply.toString()).toBe('function (a, b) {\n      //An internal comment\n      return a * b;\n    }');
  });
});
<|endoftext|>


<commit_before>var utils = require('../utils.js');
var bidfactory = require('../bidfactory.js');
var bidmanager = require('../bidmanager.js');

/**
 * Adapter for requesting bids from Pubmatic.
 *
 * @returns {{callBids: _callBids}}
 * @constructor
 */
var PubmaticAdapter = function PubmaticAdapter() {

  var bids;
  var _pm_pub_id;
  var _pm_optimize_adslots = [];
  let iframeId = '';

  function _callBids(params) {
    bids = params.bids;
    for (var i = 0; i < bids.length; i++) {
      var bid = bids[i];
      //bidmanager.pbCallbackMap['' + bid.params.adSlot] = bid;
      _pm_pub_id = _pm_pub_id || bid.params.publisherId;
      _pm_optimize_adslots.push(bid.params.adSlot);
    }

    // Load pubmatic script in an iframe, because they call document.write
    _getBids();
  }

  function _getBids() {

    //create the iframe
    var iframe = utils.createInvisibleIframe();
    iframeId = iframe.id;
    var elToAppend = document.getElementsByTagName('head')[0];

    //insert the iframe into document
    elToAppend.insertBefore(iframe, elToAppend.firstChild);

    var iframeDoc = utils.getIframeDocument(iframe);
    iframeDoc.write(_createRequestContent());
    iframeDoc.close();
  }

  function _createRequestContent() {
    var content = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"' +
      ' "http://www.w3.org/TR/html4/loose.dtd"><base target="_top" /><scr' +
      'ipt>inDapIF=true;</scr' + 'ipt>';
    content += '';
    content += '<scr' + 'ipt>';
    content += '' +
      'window.pm_pub_id  = "%%PM_PUB_ID%%";' +
      'window.pm_optimize_adslots     = [%%PM_OPTIMIZE_ADSLOTS%%];' +
      'window.pm_async_callback_fn = "window.parent.$$PREBID_GLOBAL$$.handlePubmaticCallback";';
    content += '</scr' + 'ipt>';

    var map = {};
    map.PM_PUB_ID = _pm_pub_id;
    map.PM_OPTIMIZE_ADSLOTS = _pm_optimize_adslots.map(function (adSlot) {
      return "'" + adSlot + "'";
    }).join(',');

    content += '<scr' + 'ipt src="https://ads.pubmatic.com/AdServer/js/gshowad.js"></scr' + 'ipt>';
    content += '<scr' + 'ipt>';
    content += '</scr' + 'ipt>';
    content += '';
    content = utils.replaceTokenInString(content, map, '%%');

    return content;
  }

  $$PREBID_GLOBAL$$.handlePubmaticCallback = function () {
    let bidDetailsMap = {};
    let progKeyValueMap = {};
    try {
      bidDetailsMap = window.frames[iframeId].contentWindow.bidDetailsMap;
      progKeyValueMap = window.frames[iframeId].contentWindow.progKeyValueMap;
    }
    catch(e) {
      utils.logError(e, 'Error parsing Pubmatic response');
    }

    var i;
    var adUnit;
    var adUnitInfo;
    var bid;
    var bidResponseMap = bidDetailsMap;
    var bidInfoMap = progKeyValueMap;
    var dimensions;

    for (i = 0; i < bids.length; i++) {
      var adResponse;
      bid = bids[i].params;

      adUnit = bidResponseMap[bid.adSlot] || {};

      // adUnitInfo example: bidstatus=0;bid=0.0000;bidid=39620189@320x50;wdeal=

      // if using DFP GPT, the params string comes in the format:
      // "bidstatus;1;bid;5.0000;bidid;hb_test@468x60;wdeal;"
      // the code below detects and handles this.
      if (bidInfoMap[bid.adSlot] && bidInfoMap[bid.adSlot].indexOf('=') === -1) {
        bidInfoMap[bid.adSlot] = bidInfoMap[bid.adSlot].replace(/([a-z]+);(.[^;]*)/ig, '$1=$2');
      }

      adUnitInfo = (bidInfoMap[bid.adSlot] || '').split(';').reduce(function (result, pair) {
        var parts = pair.split('=');
        result[parts[0]] = parts[1];
        return result;
      }, {});

      if (adUnitInfo.bidstatus === '1') {
        dimensions = adUnitInfo.bidid.split('@')[1].split('x');
        adResponse = bidfactory.createBid(1);
        adResponse.bidderCode = 'pubmatic';
        adResponse.adSlot = bid.adSlot;
        adResponse.cpm = Number(adUnitInfo.bid);
        adResponse.ad = unescape(adUnit.creative_tag);  // jshint ignore:line
        adResponse.ad += utils.createTrackPixelHtml(decodeURIComponent(adUnit.tracking_url));
        adResponse.width = dimensions[0];
        adResponse.height = dimensions[1];
        adResponse.dealId = adUnitInfo.wdeal;

        bidmanager.addBidResponse(bids[i].placementCode, adResponse);
      } else {
        // Indicate an ad was not returned
        adResponse = bidfactory.createBid(2);
        adResponse.bidderCode = 'pubmatic';
        bidmanager.addBidResponse(bids[i].placementCode, adResponse);
      }
    }
  };

  return {
    callBids: _callBids
  };

};

module.exports = PubmaticAdapter;
<commit_msg>Fixed error in safari (#555)<commit_after>var utils = require('../utils.js');
var bidfactory = require('../bidfactory.js');
var bidmanager = require('../bidmanager.js');

/**
 * Adapter for requesting bids from Pubmatic.
 *
 * @returns {{callBids: _callBids}}
 * @constructor
 */
var PubmaticAdapter = function PubmaticAdapter() {

  var bids;
  var _pm_pub_id;
  var _pm_optimize_adslots = [];
  let iframe;

  function _callBids(params) {
    bids = params.bids;
    for (var i = 0; i < bids.length; i++) {
      var bid = bids[i];
      //bidmanager.pbCallbackMap['' + bid.params.adSlot] = bid;
      _pm_pub_id = _pm_pub_id || bid.params.publisherId;
      _pm_optimize_adslots.push(bid.params.adSlot);
    }

    // Load pubmatic script in an iframe, because they call document.write
    _getBids();
  }

  function _getBids() {

    //create the iframe
    iframe = utils.createInvisibleIframe();

    var elToAppend = document.getElementsByTagName('head')[0];

    //insert the iframe into document
    elToAppend.insertBefore(iframe, elToAppend.firstChild);

    var iframeDoc = utils.getIframeDocument(iframe);
    iframeDoc.write(_createRequestContent());
    iframeDoc.close();
  }

  function _createRequestContent() {
    var content = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"' +
      ' "http://www.w3.org/TR/html4/loose.dtd"><base target="_top" /><scr' +
      'ipt>inDapIF=true;</scr' + 'ipt>';
    content += '';
    content += '<scr' + 'ipt>';
    content += '' +
      'window.pm_pub_id  = "%%PM_PUB_ID%%";' +
      'window.pm_optimize_adslots     = [%%PM_OPTIMIZE_ADSLOTS%%];' +
      'window.pm_async_callback_fn = "window.parent.$$PREBID_GLOBAL$$.handlePubmaticCallback";';
    content += '</scr' + 'ipt>';

    var map = {};
    map.PM_PUB_ID = _pm_pub_id;
    map.PM_OPTIMIZE_ADSLOTS = _pm_optimize_adslots.map(function (adSlot) {
      return "'" + adSlot + "'";
    }).join(',');

    content += '<scr' + 'ipt src="https://ads.pubmatic.com/AdServer/js/gshowad.js"></scr' + 'ipt>';
    content += '<scr' + 'ipt>';
    content += '</scr' + 'ipt>';
    content += '';
    content = utils.replaceTokenInString(content, map, '%%');

    return content;
  }

  $$PREBID_GLOBAL$$.handlePubmaticCallback = function () {
    let bidDetailsMap = {};
    let progKeyValueMap = {};
    try {
      bidDetailsMap = iframe.contentWindow.bidDetailsMap;
      progKeyValueMap = iframe.contentWindow.progKeyValueMap;
    }
    catch(e) {
      utils.logError(e, 'Error parsing Pubmatic response');
    }

    var i;
    var adUnit;
    var adUnitInfo;
    var bid;
    var bidResponseMap = bidDetailsMap;
    var bidInfoMap = progKeyValueMap;
    var dimensions;

    for (i = 0; i < bids.length; i++) {
      var adResponse;
      bid = bids[i].params;

      adUnit = bidResponseMap[bid.adSlot] || {};

      // adUnitInfo example: bidstatus=0;bid=0.0000;bidid=39620189@320x50;wdeal=

      // if using DFP GPT, the params string comes in the format:
      // "bidstatus;1;bid;5.0000;bidid;hb_test@468x60;wdeal;"
      // the code below detects and handles this.
      if (bidInfoMap[bid.adSlot] && bidInfoMap[bid.adSlot].indexOf('=') === -1) {
        bidInfoMap[bid.adSlot] = bidInfoMap[bid.adSlot].replace(/([a-z]+);(.[^;]*)/ig, '$1=$2');
      }

      adUnitInfo = (bidInfoMap[bid.adSlot] || '').split(';').reduce(function (result, pair) {
        var parts = pair.split('=');
        result[parts[0]] = parts[1];
        return result;
      }, {});

      if (adUnitInfo.bidstatus === '1') {
        dimensions = adUnitInfo.bidid.split('@')[1].split('x');
        adResponse = bidfactory.createBid(1);
        adResponse.bidderCode = 'pubmatic';
        adResponse.adSlot = bid.adSlot;
        adResponse.cpm = Number(adUnitInfo.bid);
        adResponse.ad = unescape(adUnit.creative_tag);  // jshint ignore:line
        adResponse.ad += utils.createTrackPixelHtml(decodeURIComponent(adUnit.tracking_url));
        adResponse.width = dimensions[0];
        adResponse.height = dimensions[1];
        adResponse.dealId = adUnitInfo.wdeal;

        bidmanager.addBidResponse(bids[i].placementCode, adResponse);
      } else {
        // Indicate an ad was not returned
        adResponse = bidfactory.createBid(2);
        adResponse.bidderCode = 'pubmatic';
        bidmanager.addBidResponse(bids[i].placementCode, adResponse);
      }
    }
  };

  return {
    callBids: _callBids
  };

};

module.exports = PubmaticAdapter;
<|endoftext|>


<commit_before>Ink.requireModules(['Ink.UI.Table_1', 'Ink.UI.Common_1', 'Ink.Dom.Element_1', 'Ink.Util.Array_1', 'Ink.Dom.Selector_1', 'Ink.UI.Pagination_1', 'Ink.Net.Ajax_1', 'Ink.Util.Json_1'], function (Table, Common, InkElement, InkArray, Selector, Pagination, Ajax, Json) {
    function makeContainer() {
        return InkElement.create('div', {
            // style: 'display: none',
            setHTML: [
                '
',
                    '
',
                        '
',
                            'col1	',
                            'col2	',
                        '
',
                    '
',
                    '
',
                        '
',
                            '1	',
                            '1	',
                        '

',
                            '2	',
                            '2	',
                        '

',
                            '3	',
                            '3	',
                        '

',
                            '4	',
                            '4	',
                        '
',
                    '
',
                '
'
            ].join(''),
            insertBottom: document.body
        });
    }

    function testTable(name, testBack, options) {
        test(name, function ()  {
            var container = makeContainer();
            var table = Ink.s('table', container);
            var component = new Table(table, options || {});
            testBack(component, table, container);
        });
    }

    function itemsInColumn(tableEl, col) {
        return InkArray.map(Ink.ss('tbody tr', tableEl), function (tr) {
            var s = Ink.ss('td', tr)[col - 1].innerHTML;
            return isNaN(+s) ? s : +s;
        });
    }

    testTable('_paginate() changes page by hiding/showing elements', function (table, tableEl) {
        // Pagination works by hiding elements not visible in the current page
        equal(Ink.ss('tr.hide-all', tableEl).length, 1);
        table._paginate(2);
        equal(Ink.ss('tr.hide-all', tableEl).length, 3);
    }, { pageSize: 3 });

    testTable('Setting the page causes the _paginate function to be called', sinon.test(function (table, tableEl, container) {
        this.stub(table, '_paginate');
        var pag = Pagination.getInstance(Ink.s('nav', container));
        pag.setCurrent(1);  // 0-based
        ok(table._paginate.calledOnce);
        equal(table._paginate.lastCall.args[0], 2, 'table._paginate was called with the new page number, 1-based.');
    }), { pageSize: 3 });

    testTable('sorting', function (table, tableEl) {
        for (var line = 1; line <= 4; line++)
            Ink.s('#col2-' + line, tableEl).innerHTML = 5 - line;

        deepEqual(
            itemsInColumn(tableEl, 1),
            [1, 2, 3, 4],
            'initial order is just as in the DOM');
        deepEqual(
            itemsInColumn(tableEl, 2),
            [4, 3, 2, 1],
            'initial order is just as in the DOM');

        var headers = Ink.ss('thead th', tableEl);
        var header1 = headers[0];
        var header2 = headers[1];

        stop();

        // TODO refactor so as to add a sort() method, so we don't have to fake events
        Syn.click(header1, function () {
            deepEqual(
                itemsInColumn(tableEl, 1),
                [1, 2, 3, 4],
                'numbers in column 1 are sorted');
            deepEqual(
                itemsInColumn(tableEl, 2),
                [4, 3, 2, 1],
                'numbers in column 2 are reversed');
            Syn.click(header2, function () {
                deepEqual(
                    itemsInColumn(tableEl, 2),
                    [1, 2, 3, 4],
                    'numbers in column 2 are sorted');
                deepEqual(
                    itemsInColumn(tableEl, 1),
                    [4, 3, 2, 1],
                    'numbers in column 1 are reversed');
                start();
            });
        });
    });

    testTable('sorting letters + numbers', function (table, tableEl) {
        var header1 = Ink.ss('thead th', tableEl)[0];

        Ink.s('#col1-1', tableEl).innerHTML = 'a';
        Ink.s('#col1-2', tableEl).innerHTML = 'b';

        stop();

        Syn.click(header1, function () {
            deepEqual(
                itemsInColumn(tableEl, 1),
                ['a', 'b', 3, 4],
                'numbers in column 1 are sorted, numbers go last');

            Syn.click(header1, function () {
                deepEqual(
                    itemsInColumn(tableEl, 1),
                    [4, 3, 'b', 'a'],
                    'numbers in column 1 are reversed, numbers go first');
                start();
            });
        });
    });

    test('Ajax table', function ()  {
        var container = makeContainer();
        InkElement.setHTML(Ink.s('tbody', container), '')
        var tableEl = Ink.s('table', container);

        var createEndpointURL = sinon.stub().returns('/some/url')

        var processJSONRows = sinon.spy(
            Table._optionDefinition.processJSONRows[1]);
        var processJSONRow = sinon.spy(
            Table._optionDefinition.processJSONRow[1]);
        var processJSONField = sinon.spy(
            Table._optionDefinition.processJSONField[1]);
        var processJSONHeaders = sinon.spy(
            Table._optionDefinition.processJSONHeaders[1]);
        var processJSONTotalRows = sinon.spy(
            Table._optionDefinition.processJSONTotalRows[1]);

        var pagElm = document.createElement('nav')

        sinon.stub(Ajax.prototype, 'init')
        var table = new Table(tableEl, {
            pageSize: 2,
            pagination: pagElm,
            createEndpointURL: createEndpointURL,
            processJSONRows,
            processJSONRow,
            processJSONField,
            processJSONHeaders,
            processJSONTotalRows
        });

        ok(!table._markupMode)

        ok(createEndpointURL.calledOnce)
        deepEqual(createEndpointURL.lastCall.args, [
            null,
            null,  // sortOrder
            {
                page: 1,  // Pages start in 1
                size: 2   // pageSize
            }
        ])
        ok(Ajax.prototype.init.calledWith('/some/url'))

        var onSuccess = Ajax.prototype.init.lastCall.args[1].onSuccess;

        var res;
        var resFields;
        var resRows;
        var resTotalRows;

        onSuccess({
            status: 200,
            responseText: Json.stringify((res = {
                fields: (resFields = [
                    'col1',
                    'col2'
                ]),
                rows: (resRows = [
                    {
                       col1: '1',
                       col2: 'a'
                    },
                    {
                       col1: '2',
                       col2: 'b'
                    }
                ]),
                totalRows: (resTotalRows = 2)
            }))
        });

        deepEqual(
            itemsInColumn(tableEl, 1),
            [1, 2],
            'data came from the ajax request!');

        // Now we look at the options we gave it and see if they were called with what we expect
        ok(processJSONRows.calledWith(res))
        ok(processJSONRow.calledTwice)
        ok(processJSONRow.calledWith(resRows[0]))
        ok(processJSONRow.calledWith(resRows[1]))
        ok(processJSONField.getCalls().length === 4)
        ok(processJSONHeaders.notCalled, 'processJSONHeaders not called unless there\'s no headers in the DOM')
        ok(processJSONTotalRows.calledWith(res))

        // Resetting the counters
        Ajax.prototype.init.reset()
        createEndpointURL.reset()

        onSuccess({
            status: 200,
            responseText: Json.stringify((res = {
                fields: (resFields = [
                    'col1',
                    'col2'
                ]),
                rows: (resRows = [
                    {
                       col1: '1',
                       col2: 'a'
                    },
                    {
                       col1: '2',
                       col2: 'b'
                    }
                ]),
                totalRows: (resTotalRows = 2)
            }))
        });
        stop()
        var header1 = Ink.ss('thead th', tableEl)[0];
        Syn.click(header1, function () {
            ok(Ajax.prototype.init.calledOnce)
            ok(createEndpointURL.calledOnce)

            deepEqual(createEndpointURL.lastCall.args, [
                null,
                {
                    field: 'col1',
                    order: 'asc'
                },
                {
                    page: 1,
                    size: 2
                }
            ])

            var onSuccess = Ajax.prototype.init.lastCall.args[1].onSuccess;
            onSuccess({
                status: 200,
                responseText: Json.stringify(({
                    fields: ([
                        'col1',
                        'col2'
                    ]),
                    rows: ([
                        {
                           col1: '2',
                           col2: 'b'
                        },
                        {
                           col1: '1',
                           col2: 'a'
                        }
                    ]),
                    totalRows: (2)
                }))
            });

            deepEqual(
                itemsInColumn(tableEl, 1),
                [2, 1],
                'data came from the ajax request! Again!');

            // Sorry Ajax, we leave you alone now
            Ajax.prototype.init.restore();
            start();
        })
    });
});

<commit_msg>back to es5 :)<commit_after>Ink.requireModules(['Ink.UI.Table_1', 'Ink.UI.Common_1', 'Ink.Dom.Element_1', 'Ink.Util.Array_1', 'Ink.Dom.Selector_1', 'Ink.UI.Pagination_1', 'Ink.Net.Ajax_1', 'Ink.Util.Json_1'], function (Table, Common, InkElement, InkArray, Selector, Pagination, Ajax, Json) {
    function makeContainer() {
        return InkElement.create('div', {
            // style: 'display: none',
            setHTML: [
                '
',
                    '
',
                        '
',
                            'col1	',
                            'col2	',
                        '
',
                    '
',
                    '
',
                        '
',
                            '1	',
                            '1	',
                        '

',
                            '2	',
                            '2	',
                        '

',
                            '3	',
                            '3	',
                        '

',
                            '4	',
                            '4	',
                        '
',
                    '
',
                '
'
            ].join(''),
            insertBottom: document.body
        });
    }

    function testTable(name, testBack, options) {
        test(name, function ()  {
            var container = makeContainer();
            var table = Ink.s('table', container);
            var component = new Table(table, options || {});
            testBack(component, table, container);
        });
    }

    function itemsInColumn(tableEl, col) {
        return InkArray.map(Ink.ss('tbody tr', tableEl), function (tr) {
            var s = Ink.ss('td', tr)[col - 1].innerHTML;
            return isNaN(+s) ? s : +s;
        });
    }

    testTable('_paginate() changes page by hiding/showing elements', function (table, tableEl) {
        // Pagination works by hiding elements not visible in the current page
        equal(Ink.ss('tr.hide-all', tableEl).length, 1);
        table._paginate(2);
        equal(Ink.ss('tr.hide-all', tableEl).length, 3);
    }, { pageSize: 3 });

    testTable('Setting the page causes the _paginate function to be called', sinon.test(function (table, tableEl, container) {
        this.stub(table, '_paginate');
        var pag = Pagination.getInstance(Ink.s('nav', container));
        pag.setCurrent(1);  // 0-based
        ok(table._paginate.calledOnce);
        equal(table._paginate.lastCall.args[0], 2, 'table._paginate was called with the new page number, 1-based.');
    }), { pageSize: 3 });

    testTable('sorting', function (table, tableEl) {
        for (var line = 1; line <= 4; line++)
            Ink.s('#col2-' + line, tableEl).innerHTML = 5 - line;

        deepEqual(
            itemsInColumn(tableEl, 1),
            [1, 2, 3, 4],
            'initial order is just as in the DOM');
        deepEqual(
            itemsInColumn(tableEl, 2),
            [4, 3, 2, 1],
            'initial order is just as in the DOM');

        var headers = Ink.ss('thead th', tableEl);
        var header1 = headers[0];
        var header2 = headers[1];

        stop();

        // TODO refactor so as to add a sort() method, so we don't have to fake events
        Syn.click(header1, function () {
            deepEqual(
                itemsInColumn(tableEl, 1),
                [1, 2, 3, 4],
                'numbers in column 1 are sorted');
            deepEqual(
                itemsInColumn(tableEl, 2),
                [4, 3, 2, 1],
                'numbers in column 2 are reversed');
            Syn.click(header2, function () {
                deepEqual(
                    itemsInColumn(tableEl, 2),
                    [1, 2, 3, 4],
                    'numbers in column 2 are sorted');
                deepEqual(
                    itemsInColumn(tableEl, 1),
                    [4, 3, 2, 1],
                    'numbers in column 1 are reversed');
                start();
            });
        });
    });

    testTable('sorting letters + numbers', function (table, tableEl) {
        var header1 = Ink.ss('thead th', tableEl)[0];

        Ink.s('#col1-1', tableEl).innerHTML = 'a';
        Ink.s('#col1-2', tableEl).innerHTML = 'b';

        stop();

        Syn.click(header1, function () {
            deepEqual(
                itemsInColumn(tableEl, 1),
                ['a', 'b', 3, 4],
                'numbers in column 1 are sorted, numbers go last');

            Syn.click(header1, function () {
                deepEqual(
                    itemsInColumn(tableEl, 1),
                    [4, 3, 'b', 'a'],
                    'numbers in column 1 are reversed, numbers go first');
                start();
            });
        });
    });

    test('Ajax table', function ()  {
        var container = makeContainer();
        InkElement.setHTML(Ink.s('tbody', container), '')
        var tableEl = Ink.s('table', container);

        var createEndpointURL = sinon.stub().returns('/some/url')

        var processJSONRows = sinon.spy(
            Table._optionDefinition.processJSONRows[1]);
        var processJSONRow = sinon.spy(
            Table._optionDefinition.processJSONRow[1]);
        var processJSONField = sinon.spy(
            Table._optionDefinition.processJSONField[1]);
        var processJSONHeaders = sinon.spy(
            Table._optionDefinition.processJSONHeaders[1]);
        var processJSONTotalRows = sinon.spy(
            Table._optionDefinition.processJSONTotalRows[1]);

        var pagElm = document.createElement('nav')

        sinon.stub(Ajax.prototype, 'init')
        var table = new Table(tableEl, {
            pageSize: 2,
            pagination: pagElm,
            createEndpointURL: createEndpointURL,
            processJSONRows: processJSONRows,
            processJSONRow: processJSONRow,
            processJSONField: processJSONField,
            processJSONHeaders: processJSONHeaders,
            processJSONTotalRows: processJSONTotalRows
        });

        ok(!table._markupMode)

        ok(createEndpointURL.calledOnce)
        deepEqual(createEndpointURL.lastCall.args, [
            null,
            null,  // sortOrder
            {
                page: 1,  // Pages start in 1
                size: 2   // pageSize
            }
        ])
        ok(Ajax.prototype.init.calledWith('/some/url'))

        var onSuccess = Ajax.prototype.init.lastCall.args[1].onSuccess;

        var res;
        var resFields;
        var resRows;
        var resTotalRows;

        onSuccess({
            status: 200,
            responseText: Json.stringify((res = {
                fields: (resFields = [
                    'col1',
                    'col2'
                ]),
                rows: (resRows = [
                    {
                       col1: '1',
                       col2: 'a'
                    },
                    {
                       col1: '2',
                       col2: 'b'
                    }
                ]),
                totalRows: (resTotalRows = 2)
            }))
        });

        deepEqual(
            itemsInColumn(tableEl, 1),
            [1, 2],
            'data came from the ajax request!');

        // Now we look at the options we gave it and see if they were called with what we expect
        ok(processJSONRows.calledWith(res))
        ok(processJSONRow.calledTwice)
        ok(processJSONRow.calledWith(resRows[0]))
        ok(processJSONRow.calledWith(resRows[1]))
        ok(processJSONField.getCalls().length === 4)
        ok(processJSONHeaders.notCalled, 'processJSONHeaders not called unless there\'s no headers in the DOM')
        ok(processJSONTotalRows.calledWith(res))

        // Resetting the counters
        Ajax.prototype.init.reset()
        createEndpointURL.reset()

        onSuccess({
            status: 200,
            responseText: Json.stringify((res = {
                fields: (resFields = [
                    'col1',
                    'col2'
                ]),
                rows: (resRows = [
                    {
                       col1: '1',
                       col2: 'a'
                    },
                    {
                       col1: '2',
                       col2: 'b'
                    }
                ]),
                totalRows: (resTotalRows = 2)
            }))
        });
        stop()
        var header1 = Ink.ss('thead th', tableEl)[0];
        Syn.click(header1, function () {
            ok(Ajax.prototype.init.calledOnce)
            ok(createEndpointURL.calledOnce)

            deepEqual(createEndpointURL.lastCall.args, [
                null,
                {
                    field: 'col1',
                    order: 'asc'
                },
                {
                    page: 1,
                    size: 2
                }
            ])

            var onSuccess = Ajax.prototype.init.lastCall.args[1].onSuccess;
            onSuccess({
                status: 200,
                responseText: Json.stringify(({
                    fields: ([
                        'col1',
                        'col2'
                    ]),
                    rows: ([
                        {
                           col1: '2',
                           col2: 'b'
                        },
                        {
                           col1: '1',
                           col2: 'a'
                        }
                    ]),
                    totalRows: (2)
                }))
            });

            deepEqual(
                itemsInColumn(tableEl, 1),
                [2, 1],
                'data came from the ajax request! Again!');

            // Sorry Ajax, we leave you alone now
            Ajax.prototype.init.restore();
            start();
        })
    });
});

<|endoftext|>