<commit_before>Zotero.Cite = function(){}
Zotero.Cite.System = function(){};

/**
 * Mappings for names
 * Note that this is the reverse of the text variable map, since all mappings should be one to one
 * and it makes the code cleaner
 */
Zotero.Cite.System._zoteroNameMap = {
	"author":"author",
	"editor":"editor",
	"translator":"translator",
	"seriesEditor":"collection-editor",
	"bookAuthor":"container-author"
}

/**
 * Mappings for text variables
 */
Zotero.Cite.System._zoteroFieldMap = {
	"title":["title"],
	"container-title":["publicationTitle",  "reporter", "code"], /* reporter and code should move to SQL mapping tables */
	"collection-title":["seriesTitle", "series"],
	"collection-number":["seriesNumber"],
	"publisher":["publisher", "distributor"], /* distributor should move to SQL mapping tables */
	"publisher-place":["place"],
	"authority":["court"],
	"page":["pages"],
	"volume":["volume"],
	"issue":["issue"],
	"number-of-volumes":["numberOfVolumes"],
	"edition":["edition"],
	"version":["version"],
	"section":["section"],
	"genre":["type", "artworkSize"], /* artworkSize should move to SQL mapping tables, or added as a CSL variable */
	"medium":["medium"],
	"archive":["archive"],
	"archive_location":["archiveLocation"],
	"event":["meetingName", "conferenceName"], /* these should be mapped to the same base field in SQL mapping tables */
	"event-place":["place"],
	"abstract":["abstractNote"],
	"URL":["url"],
	"DOI":["DOI"],
	"ISBN":["ISBN"],
	"call-number":["callNumber"],
	"note":["extra"],
	"number":["number"],
	"references":["history"],
	"shortTitle":["shortTitle"],
	"journalAbbreviation":["journalAbbreviation"]
}

Zotero.Cite.System._zoteroDateMap = {
	"issued":"date",
	"accessed":"accessDate"
}

Zotero.Cite.System._zoteroTypeMap = {
	'book':"book",
	'bookSection':'chapter',
	'journalArticle':"article-journal",
	'magazineArticle':"article-magazine",
	'newspaperArticle':"article-newspaper",
	'thesis':"thesis",
	'encyclopediaArticle':"entry-encyclopedia",
	'dictionaryEntry':"entry-dictionary",
	'conferencePaper':"paper-conference",
	'letter':"personal_communication",
	'manuscript':"manuscript",
	'interview':"interview",
	'film':"motion_picture",
	'artwork':"graphic",
	'webpage':"webpage",
	'report':"report",
	'bill':"bill",
	'case':"legal_case",
	'hearing':"bill",				// ??
	'patent':"patent",
	'statute':"bill",				// ??
	'email':"personal_communication",
	'map':"map",
	'blogPost':"webpage",
	'instantMessage':"personal_communication",
	'forumPost':"webpage",
	'audioRecording':"song",		// ??
	'presentation':"speech",
	'videoRecording':"motion_picture",
	'tvBroadcast':"broadcast",
	'radioBroadcast':"broadcast",
	'podcast':"song",			// ??
	'computerProgram':"book"		// ??
};

Zotero.Cite.System._quotedRegexp = /^".+"$/;

// TODO: Clear this cache from time to time
Zotero.Cite.System._cache = new Object();

Zotero.Cite.System.retrieveItem = function(item){
	if(item instanceof Zotero.Item) {
		//if(this._cache[item.id]) return this._cache[item.id];
		var zoteroItem = item;
	} else {
		// is an item ID
		//if(this._cache[item]) return this._cache[item];
		var zoteroItem = Zotero.Items.get(item);
	}

	if(!zoteroItem) {
		throw "Zotero.Cite.getCSLItem called to wrap a non-item";
	}

	// don't return URL or accessed information for journal articles if a
	// pages field exists
	var itemType = Zotero.ItemTypes.getName(zoteroItem.itemTypeID);
	var cslType = Zotero.Cite.System._zoteroTypeMap[itemType];
	if(!cslType) cslType = "article";
	var ignoreURL = ((zoteroItem.getField("accessDate", true, true) || zoteroItem.getField("url", true, true)) &&
			["journalArticle", "newspaperArticle", "magazineArticle"].indexOf(itemType) !== -1
			&& zoteroItem.getField("pages")
			&& !Zotero.Prefs.get("export.citePaperJournalArticleURL"));

	var cslItem = {
		'id':zoteroItem.id,
		'type':cslType
	};

	// get all text variables (there must be a better way)
	// TODO: does citeproc-js permit short forms?
	for(var variable in Zotero.Cite.System._zoteroFieldMap) {
		var fields = Zotero.Cite.System._zoteroFieldMap[variable];
		if(variable == "URL" && ignoreURL) continue;
		for each(var field in fields) {
			var value = zoteroItem.getField(field, false, true).toString();
			if(value != "") {
				// Strip enclosing quotes
				if(value.match(Zotero.Cite.System._quotedRegexp)) {
					value = value.substr(1, value.length-2);
				}
				cslItem[variable] = value;
				break;
			}
		}
	}

	// separate name variables
	var authorID = Zotero.CreatorTypes.getPrimaryIDForType(zoteroItem.itemTypeID);
	var creators = zoteroItem.getCreators();
	for each(var creator in creators) {
		if(creator.creatorTypeID == authorID) {
			var creatorType = "author";
		} else {
			var creatorType = Zotero.CreatorTypes.getName(creator.creatorTypeID);
		}

		var creatorType = Zotero.Cite.System._zoteroNameMap[creatorType];
		if(!creatorType) continue;

		var nameObj = {'family':creator.ref.lastName, 'given':creator.ref.firstName};

		if(cslItem[creatorType]) {
			cslItem[creatorType].push(nameObj);
		} else {
			cslItem[creatorType] = [nameObj];
		}
	}

	// get date variables
	for(var variable in Zotero.Cite.System._zoteroDateMap) {
		var date = zoteroItem.getField(Zotero.Cite.System._zoteroDateMap[variable], false, true);
		if(date) {
			date = Zotero.Date.strToDate(date);
			if(date.part && !date.month) {
				// if there's a part but no month, interpret literally
				cslItem[variable] = {"literal": date.part};
			} else {
				// otherwise, use date-parts
				var dateParts = [];
				if(date.year) {
					dateParts.push(date.year);
					if(date.month) {
						dateParts.push(date.month+1);
						if(date.day) {
							dateParts.push(date.day);
						}
					}
				}
				cslItem[variable] = {"date-parts":[dateParts]};
			}
		}
	}

	//this._cache[zoteroItem.id] = cslItem;
	return cslItem;
};

Zotero.Cite.System.retrieveLocale = function(lang) {
	var xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
	xhr.open("GET", "chrome://zotero/content/locale/csl/locales-"+lang+".xml", false);
	xhr.overrideMimeType("application/octet-stream");
	try {
		xhr.send();
		return xhr.responseText;
	} catch(e) {
		return false;
	}
};

Zotero.Cite.System.getAbbreviations = function() {
	return {};
}

Zotero.Cite.removeFromBibliography = function(bib, itemsToRemove) {
	var removeItems = [];
	for(let i in bib[0].entry_ids) {
		for(let j in bib[0].entry_ids[i]) {
			if(itemsToRemove[bib[0].entry_ids[i][j]]) {
				removeItems.push(i);
				break;
			}
		}
	}
	for(let i=removeItems.length-1; i>=0; i--) {
		bib[0].entry_ids.splice(removeItems[i], 1);
		bib[1].splice(removeItems[i], 1);
	}
}

Zotero.Cite.getBibliographyFormatParameters = function(bib) {
	var bibStyle = {"tabStops":[], "indent":0, "firstLineIndent":0,
	                "lineSpacing":(240*bib[0].linespacing),
	                "entrySpacing":(240*bib[0].entryspacing)};
	if(bib[0].hangingindent) {
		bibStyle.indent = 720;				// 720 twips = 0.5 in
		bibStyle.firstLineIndent = -720;	// -720 twips = -0.5 in
	} else if(bib[0]["second-field-align"]) {
		// this is a really sticky issue. the below works for first fields that look like "[1]"
		// and "1." otherwise, i have no idea. luckily, this will be good enough 99% of the time.
		var alignAt = 24+bib[0].maxoffset*120;
		bibStyle.firstLineIndent = -alignAt;
		if(bib[0]["second-field-align"] == "margin") {
			bibStyle.tabStops = [0];
		} else {
			bibStyle.indent = alignAt;
			bibStyle.tabStops = [alignAt];
		}
	}

	return bibStyle;
}

Zotero.Cite.makeFormattedBibliography = function(cslEngine, format) {
	cslEngine.setOutputFormat(format);
	var bib = cslEngine.makeBibliography();

	if(format == "html") {
		// TODO CSS
		return bib[0].bibstart+bib[1].join("")+bib[0].bibend;
	} else if(format == "text") {
		return bib[0].bibstart+bib[1].join("")+bib[0].bibend;
	} else if(format == "rtf") {
		var bibStyle = Zotero.Cite.getBibliographyFormatParameters(bib);

		var preamble = (tabStops.length ? "\\tx"+tabStops.join(" \\tx")+" " : "");
		preamble += "\\li"+indent+" \\fi"+firstLineIndent+" "
		           +"\\sl"+bibStyle.lineSpacing+" \\slmult1 "
		           +"\\sa"+bibStyle.entrySpacing+" ";

		return bib[0].bibstart+preamble+bib[1].join("\\\r\n")+"\\\r\n"+bib[0].bibend;
	} else {
		throw "Unimplemented bibliography format "+format;
	}
}

Zotero.Cite.labels = ["page", "book", "chapter", "column", "figure", "folio",
		"issue", "line", "note", "opus", "paragraph", "part", "section", "sub verbo",
		"volume", "verse"];

Zotero.Cite._monthStrings = false;
Zotero.Cite.getMonthStrings = function(form, locale) {
	if(Zotero.Cite._monthStrings){
		return Zotero.Cite._monthStrings[form];
	} else {
		Zotero.Cite._monthStrings = {"long":[], "short":[]};

		var sys = {'xml':new Zotero.CiteProc.CSL.System.Xml.Parsing()};
		if(!locale) locale = Zotero.locale;

		var cslLocale = Zotero.CiteProc.CSL.localeResolve(Zotero.locale);
		if(!Zotero.CiteProc.CSL.locale[cslLocale.best]) {
			let localexml = sys.xml.makeXml(Zotero.Cite.System.retrieveLocale(cslLocale.best));
			if(!localexml) {
				if(localexml == "en-US") {
					throw "No locales.xml file could be found for the preferred locale or for en-US. "+
					      "Please ensure that the locales directory exists and is properly populated";
				} else {
					let localexml = sys.xml.makeXml(Zotero.Cite.System.retrieveLocale(cslLocale.bare));
					if(!localexml) {
						Zotero.log("No locale "+cslLocale.best+"; using en-US", "warning");
						return Zotero.Cite.getMonthStrings(form, "en-US");
					}
				}
			}
			Zotero.CiteProc.CSL.localeSet.call(Zotero.CiteProc.CSL, sys, localexml, cslLocale.best, cslLocale.best);
		}

		var locale = Zotero.CiteProc.CSL.locale[cslLocale.best];
		if(!locale) {
			Zotero.log("No locale "+cslLocale.best+"; using en-US", "warning");
			return Zotero.Cite.getMonthStrings(form, "en-US");
		}

		for(let i=1; i<=12; i++) {
			let term = locale.terms["month-"+(i<10 ? "0" : "")+i];
			if(term) {
				Zotero.Cite._monthStrings["long"][i-1] = term["long"];
				Zotero.Cite._monthStrings["short"][i-1] = (term["short"] ? term["short"].replace(".", "", "g") : term["long"]);
			} else {
				Zotero.log("No month "+i+" specified for locale "+cslLocale.best, "warning");
			}
		}
		return Zotero.Cite._monthStrings[form];
	}
}<commit_msg>fix RTF bibliography generation<commit_after>Zotero.Cite = function(){}
Zotero.Cite.System = function(){};

/**
 * Mappings for names
 * Note that this is the reverse of the text variable map, since all mappings should be one to one
 * and it makes the code cleaner
 */
Zotero.Cite.System._zoteroNameMap = {
	"author":"author",
	"editor":"editor",
	"translator":"translator",
	"seriesEditor":"collection-editor",
	"bookAuthor":"container-author"
}

/**
 * Mappings for text variables
 */
Zotero.Cite.System._zoteroFieldMap = {
	"title":["title"],
	"container-title":["publicationTitle",  "reporter", "code"], /* reporter and code should move to SQL mapping tables */
	"collection-title":["seriesTitle", "series"],
	"collection-number":["seriesNumber"],
	"publisher":["publisher", "distributor"], /* distributor should move to SQL mapping tables */
	"publisher-place":["place"],
	"authority":["court"],
	"page":["pages"],
	"volume":["volume"],
	"issue":["issue"],
	"number-of-volumes":["numberOfVolumes"],
	"edition":["edition"],
	"version":["version"],
	"section":["section"],
	"genre":["type", "artworkSize"], /* artworkSize should move to SQL mapping tables, or added as a CSL variable */
	"medium":["medium"],
	"archive":["archive"],
	"archive_location":["archiveLocation"],
	"event":["meetingName", "conferenceName"], /* these should be mapped to the same base field in SQL mapping tables */
	"event-place":["place"],
	"abstract":["abstractNote"],
	"URL":["url"],
	"DOI":["DOI"],
	"ISBN":["ISBN"],
	"call-number":["callNumber"],
	"note":["extra"],
	"number":["number"],
	"references":["history"],
	"shortTitle":["shortTitle"],
	"journalAbbreviation":["journalAbbreviation"]
}

Zotero.Cite.System._zoteroDateMap = {
	"issued":"date",
	"accessed":"accessDate"
}

Zotero.Cite.System._zoteroTypeMap = {
	'book':"book",
	'bookSection':'chapter',
	'journalArticle':"article-journal",
	'magazineArticle':"article-magazine",
	'newspaperArticle':"article-newspaper",
	'thesis':"thesis",
	'encyclopediaArticle':"entry-encyclopedia",
	'dictionaryEntry':"entry-dictionary",
	'conferencePaper':"paper-conference",
	'letter':"personal_communication",
	'manuscript':"manuscript",
	'interview':"interview",
	'film':"motion_picture",
	'artwork':"graphic",
	'webpage':"webpage",
	'report':"report",
	'bill':"bill",
	'case':"legal_case",
	'hearing':"bill",				// ??
	'patent':"patent",
	'statute':"bill",				// ??
	'email':"personal_communication",
	'map':"map",
	'blogPost':"webpage",
	'instantMessage':"personal_communication",
	'forumPost':"webpage",
	'audioRecording':"song",		// ??
	'presentation':"speech",
	'videoRecording':"motion_picture",
	'tvBroadcast':"broadcast",
	'radioBroadcast':"broadcast",
	'podcast':"song",			// ??
	'computerProgram':"book"		// ??
};

Zotero.Cite.System._quotedRegexp = /^".+"$/;

// TODO: Clear this cache from time to time
Zotero.Cite.System._cache = new Object();

Zotero.Cite.System.retrieveItem = function(item){
	if(item instanceof Zotero.Item) {
		//if(this._cache[item.id]) return this._cache[item.id];
		var zoteroItem = item;
	} else {
		// is an item ID
		//if(this._cache[item]) return this._cache[item];
		var zoteroItem = Zotero.Items.get(item);
	}

	if(!zoteroItem) {
		throw "Zotero.Cite.getCSLItem called to wrap a non-item";
	}

	// don't return URL or accessed information for journal articles if a
	// pages field exists
	var itemType = Zotero.ItemTypes.getName(zoteroItem.itemTypeID);
	var cslType = Zotero.Cite.System._zoteroTypeMap[itemType];
	if(!cslType) cslType = "article";
	var ignoreURL = ((zoteroItem.getField("accessDate", true, true) || zoteroItem.getField("url", true, true)) &&
			["journalArticle", "newspaperArticle", "magazineArticle"].indexOf(itemType) !== -1
			&& zoteroItem.getField("pages")
			&& !Zotero.Prefs.get("export.citePaperJournalArticleURL"));

	var cslItem = {
		'id':zoteroItem.id,
		'type':cslType
	};

	// get all text variables (there must be a better way)
	// TODO: does citeproc-js permit short forms?
	for(var variable in Zotero.Cite.System._zoteroFieldMap) {
		var fields = Zotero.Cite.System._zoteroFieldMap[variable];
		if(variable == "URL" && ignoreURL) continue;
		for each(var field in fields) {
			var value = zoteroItem.getField(field, false, true).toString();
			if(value != "") {
				// Strip enclosing quotes
				if(value.match(Zotero.Cite.System._quotedRegexp)) {
					value = value.substr(1, value.length-2);
				}
				cslItem[variable] = value;
				break;
			}
		}
	}

	// separate name variables
	var authorID = Zotero.CreatorTypes.getPrimaryIDForType(zoteroItem.itemTypeID);
	var creators = zoteroItem.getCreators();
	for each(var creator in creators) {
		if(creator.creatorTypeID == authorID) {
			var creatorType = "author";
		} else {
			var creatorType = Zotero.CreatorTypes.getName(creator.creatorTypeID);
		}

		var creatorType = Zotero.Cite.System._zoteroNameMap[creatorType];
		if(!creatorType) continue;

		var nameObj = {'family':creator.ref.lastName, 'given':creator.ref.firstName};

		if(cslItem[creatorType]) {
			cslItem[creatorType].push(nameObj);
		} else {
			cslItem[creatorType] = [nameObj];
		}
	}

	// get date variables
	for(var variable in Zotero.Cite.System._zoteroDateMap) {
		var date = zoteroItem.getField(Zotero.Cite.System._zoteroDateMap[variable], false, true);
		if(date) {
			date = Zotero.Date.strToDate(date);
			if(date.part && !date.month) {
				// if there's a part but no month, interpret literally
				cslItem[variable] = {"literal": date.part};
			} else {
				// otherwise, use date-parts
				var dateParts = [];
				if(date.year) {
					dateParts.push(date.year);
					if(date.month) {
						dateParts.push(date.month+1);
						if(date.day) {
							dateParts.push(date.day);
						}
					}
				}
				cslItem[variable] = {"date-parts":[dateParts]};
			}
		}
	}

	//this._cache[zoteroItem.id] = cslItem;
	return cslItem;
};

Zotero.Cite.System.retrieveLocale = function(lang) {
	var xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
	xhr.open("GET", "chrome://zotero/content/locale/csl/locales-"+lang+".xml", false);
	xhr.overrideMimeType("application/octet-stream");
	try {
		xhr.send();
		return xhr.responseText;
	} catch(e) {
		return false;
	}
};

Zotero.Cite.System.getAbbreviations = function() {
	return {};
}

Zotero.Cite.removeFromBibliography = function(bib, itemsToRemove) {
	var removeItems = [];
	for(let i in bib[0].entry_ids) {
		for(let j in bib[0].entry_ids[i]) {
			if(itemsToRemove[bib[0].entry_ids[i][j]]) {
				removeItems.push(i);
				break;
			}
		}
	}
	for(let i=removeItems.length-1; i>=0; i--) {
		bib[0].entry_ids.splice(removeItems[i], 1);
		bib[1].splice(removeItems[i], 1);
	}
}

Zotero.Cite.getBibliographyFormatParameters = function(bib) {
	var bibStyle = {"tabStops":[], "indent":0, "firstLineIndent":0,
	                "lineSpacing":(240*bib[0].linespacing),
	                "entrySpacing":(240*bib[0].entryspacing)};
	if(bib[0].hangingindent) {
		bibStyle.indent = 720;				// 720 twips = 0.5 in
		bibStyle.firstLineIndent = -720;	// -720 twips = -0.5 in
	} else if(bib[0]["second-field-align"]) {
		// this is a really sticky issue. the below works for first fields that look like "[1]"
		// and "1." otherwise, i have no idea. luckily, this will be good enough 99% of the time.
		var alignAt = 24+bib[0].maxoffset*120;
		bibStyle.firstLineIndent = -alignAt;
		if(bib[0]["second-field-align"] == "margin") {
			bibStyle.tabStops = [0];
		} else {
			bibStyle.indent = alignAt;
			bibStyle.tabStops = [alignAt];
		}
	}

	return bibStyle;
}

Zotero.Cite.makeFormattedBibliography = function(cslEngine, format) {
	cslEngine.setOutputFormat(format);
	var bib = cslEngine.makeBibliography();

	if(format == "html") {
		// TODO CSS
		return bib[0].bibstart+bib[1].join("")+bib[0].bibend;
	} else if(format == "text") {
		return bib[0].bibstart+bib[1].join("")+bib[0].bibend;
	} else if(format == "rtf") {
		var bibStyle = Zotero.Cite.getBibliographyFormatParameters(bib);

		var preamble = (bibStyle.tabStops.length ? "\\tx"+bibStyle.tabStops.join(" \\tx")+" " : "");
		preamble += "\\li"+bibStyle.indent+" \\fi"+bibStyle.firstLineIndent+" "
		           +"\\sl"+bibStyle.lineSpacing+" \\slmult1 "
		           +"\\sa"+bibStyle.entrySpacing+" ";

		return bib[0].bibstart+preamble+bib[1].join("\\\r\n")+"\\\r\n"+bib[0].bibend;
	} else {
		throw "Unimplemented bibliography format "+format;
	}
}

Zotero.Cite.labels = ["page", "book", "chapter", "column", "figure", "folio",
		"issue", "line", "note", "opus", "paragraph", "part", "section", "sub verbo",
		"volume", "verse"];

Zotero.Cite._monthStrings = false;
Zotero.Cite.getMonthStrings = function(form, locale) {
	if(Zotero.Cite._monthStrings){
		return Zotero.Cite._monthStrings[form];
	} else {
		Zotero.Cite._monthStrings = {"long":[], "short":[]};

		var sys = {'xml':new Zotero.CiteProc.CSL.System.Xml.Parsing()};
		if(!locale) locale = Zotero.locale;

		var cslLocale = Zotero.CiteProc.CSL.localeResolve(Zotero.locale);
		if(!Zotero.CiteProc.CSL.locale[cslLocale.best]) {
			let localexml = sys.xml.makeXml(Zotero.Cite.System.retrieveLocale(cslLocale.best));
			if(!localexml) {
				if(localexml == "en-US") {
					throw "No locales.xml file could be found for the preferred locale or for en-US. "+
					      "Please ensure that the locales directory exists and is properly populated";
				} else {
					let localexml = sys.xml.makeXml(Zotero.Cite.System.retrieveLocale(cslLocale.bare));
					if(!localexml) {
						Zotero.log("No locale "+cslLocale.best+"; using en-US", "warning");
						return Zotero.Cite.getMonthStrings(form, "en-US");
					}
				}
			}
			Zotero.CiteProc.CSL.localeSet.call(Zotero.CiteProc.CSL, sys, localexml, cslLocale.best, cslLocale.best);
		}

		var locale = Zotero.CiteProc.CSL.locale[cslLocale.best];
		if(!locale) {
			Zotero.log("No locale "+cslLocale.best+"; using en-US", "warning");
			return Zotero.Cite.getMonthStrings(form, "en-US");
		}

		for(let i=1; i<=12; i++) {
			let term = locale.terms["month-"+(i<10 ? "0" : "")+i];
			if(term) {
				Zotero.Cite._monthStrings["long"][i-1] = term["long"];
				Zotero.Cite._monthStrings["short"][i-1] = (term["short"] ? term["short"].replace(".", "", "g") : term["long"]);
			} else {
				Zotero.log("No month "+i+" specified for locale "+cslLocale.best, "warning");
			}
		}
		return Zotero.Cite._monthStrings[form];
	}
}<|endoftext|>


<commit_before>(function () {
    const dataPrototypeds = document.querySelectorAll('[data-prototype][data-allow-add]');
    if (dataPrototypeds.length) {
        const divTpl = document.createElement('div'),
            addDeleteLink = (dataPrototyped, collectionEntry) => {
                if (!dataPrototyped.dataset.allowDelete) {
                    return;
                }

                const spanDel = document.createElement('span')
                spanDel.classList.add('deleteFromCollectionCont');
                if (dataPrototyped.dataset.tplAdd) {
                    spanDel.innerHTML = dataPrototyped.dataset.tplAdd;
                } else {
                    spanDel.innerHTML = '' + dataPrototyped.dataset.allowDelete + '';
                }

                collectionEntry.appendChild(spanDel);
            },
            addToCollection = (dataPrototyped, divAdd) => {
                divTpl.innerHTML = dataPrototyped.dataset.prototype
                    .replace(/__name__/g, dataPrototyped.dataset.index);

                while (divTpl.lastElementChild) {
                    if (divTpl.lastElementChild.matches('.form_row_collection_entry')) {
                        addDeleteLink(dataPrototyped, divTpl.lastElementChild);
                    }
                    divAdd.insertAdjacentElement('beforebegin', divTpl.lastElementChild);
                }

                dataPrototyped.dataset.index++;
            };
        dataPrototypeds.forEach(dataPrototyped => {
            const divAdd = document.createElement('div'),
                entries = dataPrototyped.querySelectorAll('.form_row_collection_entry'),
                allowDelete = dataPrototyped.dataset.allowDelete;

            dataPrototyped.dataset.index = entries.length;
            if (allowDelete && entries.length) {
                entries.forEach(entry => {
                    addDeleteLink(dataPrototyped, entry);
                });
            }

            dataPrototyped.addEventListener('click', (e) => {
                const addBtn = e.target.closest('.addToCollection');
                if (addBtn) {
                    e.preventDefault();
                    // Add new element
                    addToCollection(dataPrototyped, divAdd);
                    return;
                }

                const delBtn = e.target.closest('.deleteFromCollection');
                if (delBtn && allowDelete) {
                    e.preventDefault();
                    if (dataPrototyped.dataset.deleteConfirm && !confirm(dataPrototyped.dataset.deleteConfirm)) {
                        return;
                    }
                    delBtn.closest('.form_row_collection_entry').remove();
                    return;
                }
            });

            divAdd.classList.add('addToCollectionCont');
            if (dataPrototyped.dataset.tplAdd) {
                divAdd.innerHTML = dataPrototyped.dataset.tplAdd;
            } else {
                divAdd.innerHTML = '' + dataPrototyped.dataset.allowAdd + '';
            }

            dataPrototyped.appendChild(divAdd);
        });
    }
})();<commit_msg>Improve formCollection JS to make it triggerable<commit_after>(function () {
    let divTpl,
        addToCollection,
        addDeleteLink;

    const dataPrototypeds = document.querySelectorAll('[data-prototype][data-allow-add]'),
        initDataPrototypeds = () => {
            if (addToCollection) {
                return;
            }
            divTpl = document.createElement('div');
            addDeleteLink = (dataPrototyped, collectionEntry) => {
                if (!dataPrototyped.dataset.allowDelete) {
                    return;
                }

                const spanDel = document.createElement('span')
                spanDel.classList.add('deleteFromCollectionCont');
                if (dataPrototyped.dataset.tplAdd) {
                    spanDel.innerHTML = dataPrototyped.dataset.tplAdd;
                } else {
                    spanDel.innerHTML = '' + dataPrototyped.dataset.allowDelete + '';
                }

                collectionEntry.appendChild(spanDel);
            };
            addToCollection = (dataPrototyped, divAdd) => {
                divTpl.innerHTML = dataPrototyped.dataset.prototype
                    .replace(/__name__/g, dataPrototyped.dataset.index);

                while (divTpl.lastElementChild) {
                    if (divTpl.lastElementChild.matches('.form_row_collection_entry')) {
                        addDeleteLink(dataPrototyped, divTpl.lastElementChild);
                    }
                    divAdd.insertAdjacentElement('beforebegin', divTpl.lastElementChild);
                }

                dataPrototyped.dataset.index++;
            };
        },
        initDataPrototyped = (dataPrototyped) => {
            initDataPrototypeds();
            const divAdd = document.createElement('div'),
                entries = dataPrototyped.querySelectorAll('.form_row_collection_entry'),
                allowDelete = dataPrototyped.dataset.allowDelete;

            dataPrototyped.dataset.index = entries.length;
            if (allowDelete && entries.length) {
                entries.forEach(entry => {
                    addDeleteLink(dataPrototyped, entry);
                });
            }

            dataPrototyped.addEventListener('click', (e) => {
                const addBtn = e.target.closest('.addToCollection');
                if (addBtn) {
                    e.preventDefault();
                    // Add new element
                    addToCollection(dataPrototyped, divAdd);
                    return;
                }

                const delBtn = e.target.closest('.deleteFromCollection');
                if (delBtn && allowDelete) {
                    e.preventDefault();
                    if (dataPrototyped.dataset.deleteConfirm && !confirm(dataPrototyped.dataset.deleteConfirm)) {
                        return;
                    }
                    delBtn.closest('.form_row_collection_entry').remove();
                    return;
                }
            });

            divAdd.classList.add('addToCollectionCont');
            if (dataPrototyped.dataset.tplAdd) {
                divAdd.innerHTML = dataPrototyped.dataset.tplAdd;
            } else {
                divAdd.innerHTML = '' + dataPrototyped.dataset.allowAdd + '';
            }

            dataPrototyped.appendChild(divAdd);
        };

    if (dataPrototypeds.length) {
        dataPrototypeds.forEach(dataPrototyped => {
            initDataPrototyped(dataPrototyped);
        });
    }

    document.body.addEventListener('initDataPrototyped', (e) => {
        initDataPrototyped(e.target);
    });

    document.body.addEventListener('searchDataPrototyped', (e) => {
        console.log(e);
        const dataPrototypeds = e.target.querySelectorAll('[data-prototype][data-allow-add]');

        if (dataPrototypeds.length) {
            dataPrototypeds.forEach(dataPrototyped => {
                initDataPrototyped(dataPrototyped);
            });
        }
    });
})();<|endoftext|>


<commit_before>$.widget("wheregroup.queryResultView", {
    options: {
        query: null
    },
    tableDefaults: {
        lengthChange: false,
        pageLength:   15,
        searching:    true,
        info:         true,
        processing:   false,
        ordering:     true,
        paging:       true,
        autoWidth:    false
    },

    /**
     * Settings for saving
     */
    preparedData:{
    },

    table: null,

    /**
     * Constructor
     */
    _create: function() {
        this.render(this.options.query);
    },

    /**
     * Render
     */
    render: function(query) {
        var widget = this;
        var element = $(widget.element);
        var options = widget.options;
        var progressBar = $("
");
        var table, tableApi;

        element
            .attr('data-id', query.id)
            .empty();

        element.generateElements(Mapbender.Util.beautifyGenerateElements({
            type:     'fieldSet',
            children: [{
                type:     'checkbox',
                cssClass: 'onlyExtent',
                title:    Mapbender.trans("mb.search.onlyMapSection"),
                checked:  query.extendOnly,
                change:   function() {
                    var input = $('input', this);
                    widget._trigger('changeExtend', null, {
                        query:   query,
                        widget:  widget,
                        element: element,
                        checked: input.is(":checked")
                    });
                }
            }]
        }));

        function escapeHtml(text) {
            'use strict';
            return text.replace(/["&'\/<>]/g, function (a) {
                return {
                    '"': '"', '&': '&', "'": ''',
                    '/': '/',  '<': '<',  '>': '>'
                }[a];
            });
        }

        var columns = [];
        var buttons = [];

        buttons.push({
            title:     Mapbender.trans("mb.search.feature.zoomTo"),
            className: 'zoomTo',
            onClick:   function(olFeature, ui) {
                widget._trigger('zoomTo', null, {
                    feature: olFeature,
                    ui:      ui,
                    query:   query,
                    widget:  widget
                })
            }
        });

        buttons.push({
            title:     Mapbender.trans("mb.search.feature.bookmark"),
            className: 'bookmark',
            onClick:   function(olFeature, ui) {
                widget._trigger('mark', null, {
                    feature: olFeature,
                    ui:      ui,
                    query:   query,
                    widget:  widget
                })
            }
        });

        buttons.push({
            title:     Mapbender.trans("mb.search.feature.toggleVisibility"),
            className: 'visibility',
            onClick:   function(olFeature, ui) {
                widget._trigger('toggleVisibility', null, {
                    feature: olFeature,
                    ui:      ui,
                    query:   query,
                    widget:  widget
                })
            }
        });

       /* buttons.push({
            title:     "Druck",
            className: 'print',
            onClick:   function(olFeature, ui) {
                widget._trigger('print', null, {
                    feature: olFeature,
                    ui:      ui,
                    query:   query,
                    widget:  widget
                })
            }
        }); */

        _.each(query.fields, function(definition) {
            columns.push({
                title: definition.title,
                data:  function(row, type, val, meta) {
                    var data = row.data[definition.fieldName];
                    if(typeof (data) == 'string') {
                        data = escapeHtml(data);
                    }
                    return data;
                }
            });
        });

        table = widget.table = $("
").resultTable(_.extend({
            columns: columns,
            buttons: buttons,
            createdRow: function(tr, feature) {
                $(tr).data({feature: feature});
                feature.tableRow = tr;
            }
        }, options.table));

        tableApi = table.resultTable('getApi');

        table.contextMenu({
            selector: 'table > tbody > tr[role="row"]',
            events:   {
                // show: function(options) {
                //     // var tr = $(options.$trigger);
                //     // var resultTable = tr.closest('.mapbender-element-result-table');
                //     // var api = resultTable.resultTable('getApi');
                //     // var olFeature = api.row(tr).data();
                //     return true;
                // }
            },
            build:    function($trigger, e) {
                var tr = $($trigger);
                var olFeature = tr.data('feature');

                var items = {};

                items['zoomTo'] = {name: "Heranzoomen"};

                return {
                    callback: function(key, options) {
                        widget._trigger(key, null, {
                            feature: olFeature
                        });
                    },
                    items:    items
                };
            }
        });

        table.find("tbody")
            .off('click', '> tr')
            .on('click', '> tr[role="row"]', function(e) {
                if ($(e.target).parentsUntil(this).is('.buttons')) {
                    return;
                }
                var olFeature = $(this).data('feature');
                if(!olFeature) {
                    return;
                }

                widget._trigger('featureClick', null, {
                    feature:  olFeature,
                    ui: this,
                    query: query
                });
                return false;
            })
            .off('mouseover', '> tr')
            .on('mouseover', '> tr[role="row"]', function(e) {
                if ($(e.target).parentsUntil(this).is('.buttons')) {
                    return;
                }
                var olFeature = $(this).data('feature');
                if(!olFeature) {
                    return false;
                }

                widget._trigger('featureOver', null, {
                    feature:  olFeature
                });
                return false;
            })
            .off('mouseout', '> tr')
            .on('mouseout', '> tr[role="row"]', function(e) {
                if ($(e.target).parentsUntil(this).is('.buttons')) {
                    return;
                }
                var olFeature = $(this).data('feature');
                if(!olFeature) {
                    return false;
                }

                widget._trigger('featureOut', null, {
                    feature:  olFeature
                });
                return false;
            });

        element.append(table);
        element.append(progressBar);

        // Add placeholder to result table filter search input
        $('input[type="search"]', table).attr('placeholder', _.pluck(query.fields, 'title').join(', '));

        return element;
    },

    updateList: function(list) {
        var $table = $('table:first', this.table);
        var tableApi = $table.dataTable().api();
        tableApi.clear();
        tableApi.rows.add(list);
        tableApi.draw();

        // Redraw page
        // tableApi.draw({"paging": "page"});

        // update data item
        // tableApi.row(item).invalidate().draw();
    },

    /**
     * Close and remove widget
     */
    close: function() {
        var widget = this;
        var element = $(widget.element);
        var options = widget.optionás;

        widget.element.remove();
    }
});

<commit_msg>Start vis-ui resultTable decoupling<commit_after>$.widget("wheregroup.queryResultView", {
    options: {
        query: null
    },
    tableDefaults: {
        lengthChange: false,
        pageLength:   15,
        searching:    true,
        info:         true,
        processing:   false,
        ordering:     true,
        paging:       true,
        autoWidth:    false
    },

    /**
     * Settings for saving
     */
    preparedData:{
    },

    table: null,

    /**
     * Constructor
     */
    _create: function() {
        this.render(this.options.query);
    },

    /**
     * Render
     */
    render: function(query) {
        var widget = this;
        var element = $(widget.element);
        var options = widget.options;
        var progressBar = $("
");
        var table, tableApi;

        element
            .attr('data-id', query.id)
            .empty();

        element.generateElements(Mapbender.Util.beautifyGenerateElements({
            type:     'fieldSet',
            children: [{
                type:     'checkbox',
                cssClass: 'onlyExtent',
                title:    Mapbender.trans("mb.search.onlyMapSection"),
                checked:  query.extendOnly,
                change:   function() {
                    var input = $('input', this);
                    widget._trigger('changeExtend', null, {
                        query:   query,
                        widget:  widget,
                        element: element,
                        checked: input.is(":checked")
                    });
                }
            }]
        }));

        function escapeHtml(text) {
            'use strict';
            return text.replace(/["&'\/<>]/g, function (a) {
                return {
                    '"': '"', '&': '&', "'": ''',
                    '/': '/',  '<': '<',  '>': '>'
                }[a];
            });
        }

        var columns = [];
        var buttons = [];

        buttons.push({
            title:     Mapbender.trans("mb.search.feature.zoomTo"),
            className: 'zoomTo',
            onClick:   function(olFeature, ui) {
                widget._trigger('zoomTo', null, {
                    feature: olFeature,
                    ui:      ui,
                    query:   query,
                    widget:  widget
                })
            }
        });

        buttons.push({
            title:     Mapbender.trans("mb.search.feature.bookmark"),
            className: 'bookmark',
            onClick:   function(olFeature, ui) {
                widget._trigger('mark', null, {
                    feature: olFeature,
                    ui:      ui,
                    query:   query,
                    widget:  widget
                })
            }
        });

        buttons.push({
            title:     Mapbender.trans("mb.search.feature.toggleVisibility"),
            className: 'visibility',
            onClick:   function(olFeature, ui) {
                widget._trigger('toggleVisibility', null, {
                    feature: olFeature,
                    ui:      ui,
                    query:   query,
                    widget:  widget
                })
            }
        });

       /* buttons.push({
            title:     "Druck",
            className: 'print',
            onClick:   function(olFeature, ui) {
                widget._trigger('print', null, {
                    feature: olFeature,
                    ui:      ui,
                    query:   query,
                    widget:  widget
                })
            }
        }); */

        _.each(query.fields, function(definition) {
            columns.push({
                title: definition.title,
                data:  function(row, type, val, meta) {
                    var data = row.data[definition.fieldName];
                    if(typeof (data) == 'string') {
                        data = escapeHtml(data);
                    }
                    return data;
                }
            });
        });

        var tableOptions = _.extend({
            columns: columns,
            buttons: buttons,
            createdRow: function(tr, feature) {
                $(tr).data({feature: feature});
                feature.tableRow = tr;
            }
        }, this.tableDefaults)
        table = this.table = $("
").resultTable(tableOptions);

        table.find("tbody")
            .off('click', '> tr')
            .on('click', '> tr[role="row"]', function(e) {
                if ($(e.target).parentsUntil(this).is('.buttons')) {
                    return;
                }
                var olFeature = $(this).data('feature');
                if(!olFeature) {
                    return;
                }

                widget._trigger('featureClick', null, {
                    feature:  olFeature,
                    ui: this,
                    query: query
                });
                return false;
            })
            .off('mouseover', '> tr')
            .on('mouseover', '> tr[role="row"]', function(e) {
                if ($(e.target).parentsUntil(this).is('.buttons')) {
                    return;
                }
                var olFeature = $(this).data('feature');
                if(!olFeature) {
                    return false;
                }

                widget._trigger('featureOver', null, {
                    feature:  olFeature
                });
                return false;
            })
            .off('mouseout', '> tr')
            .on('mouseout', '> tr[role="row"]', function(e) {
                if ($(e.target).parentsUntil(this).is('.buttons')) {
                    return;
                }
                var olFeature = $(this).data('feature');
                if(!olFeature) {
                    return false;
                }

                widget._trigger('featureOut', null, {
                    feature:  olFeature
                });
                return false;
            });

        element.append(table);
        element.append(progressBar);

        // Add placeholder to result table filter search input
        $('input[type="search"]', table).attr('placeholder', _.pluck(query.fields, 'title').join(', '));

        return element;
    },

    updateList: function(list) {
        var $table = $('table:first', this.table);
        var tableApi = $table.dataTable().api();
        tableApi.clear();
        tableApi.rows.add(list);
        tableApi.draw();

        // Redraw page
        // tableApi.draw({"paging": "page"});

        // update data item
        // tableApi.row(item).invalidate().draw();
    },

    /**
     * Close and remove widget
     */
    close: function() {
        var widget = this;
        var element = $(widget.element);
        var options = widget.optionás;

        widget.element.remove();
    }
});

<|endoftext|>


<commit_before>console.log("goa: GOA content script for GMail loading");

function detectLoginInfo() {
  console.log("goa: Scraping for login info");
  var r = document.evaluate('//*[@role="navigation"]//text()[contains(.,"@")]', document.body, null, XPathResult.STRING_TYPE, null);
  var loginName = r.stringValue;
  if (!loginName)
    return false;
  var msg = {
    type: 'login-detected',
    data: {
      identity: loginName,
      provider: 'google',
      services: ['mail'],
      authenticationDomain: 'google.com'
    }
  };
  console.log("goa: Detected login info:", msg);
  chrome.extension.sendMessage(msg);
  return true;
}

// Run detectLoginInfo() on any DOM change if not ready yet
if (!detectLoginInfo()) {
  var t = {};
  t.observer = new WebKitMutationObserver(function(mutations, observer) {
    if (detectLoginInfo()) {
      observer.disconnect();
      clearTimeout(t.timeout);
    }
  });
  t.observer.observe(document.body, { childList: true, subtree: true, characterData: true });
  t.timeout = setTimeout(function() {
      console.log("goa: Give up, unable to find any login info");
      t.observer.disconnect();
  }, 10000);
};
<commit_msg>chromium: Make login name detection more strict<commit_after>console.log("goa: GOA content script for GMail loading");

function detectLoginInfo() {
  console.log("goa: Scraping for login info");
  var selector = '//*[@role="navigation"]//*[starts-with(@href, "https://profiles.google.com/")]//text()[contains(.,"@")]';
  var r = document.evaluate(selector, document.body, null, XPathResult.STRING_TYPE, null);
  var loginName = r.stringValue;
  if (!loginName)
    return false;
  var msg = {
    type: 'login-detected',
    data: {
      identity: loginName,
      provider: 'google',
      services: ['mail'],
      authenticationDomain: 'google.com'
    }
  };
  console.log("goa: Detected login info:", msg);
  chrome.extension.sendMessage(msg);
  return true;
}

// Run detectLoginInfo() on any DOM change if not ready yet
if (!detectLoginInfo()) {
  var t = {};
  t.observer = new WebKitMutationObserver(function(mutations, observer) {
    if (detectLoginInfo()) {
      observer.disconnect();
      clearTimeout(t.timeout);
    }
  });
  t.observer.observe(document.body, { childList: true, subtree: true, characterData: true });
  t.timeout = setTimeout(function() {
      console.log("goa: Give up, unable to find any login info");
      t.observer.disconnect();
  }, 10000);
};
<|endoftext|>


<commit_before>// jQuery File Tree Plugin
//
// Version 1.01
//
// Cory S.N. LaViska
// A Beautiful Site (http://abeautifulsite.net/)
// 24 March 2008
//
// Visit http://abeautifulsite.net/notebook.php?article=58 for more information
//
// Usage: $('.fileTreeDemo').fileTree( options, callback )
//
// Options:  root           - root folder to display; default = /
//           script         - location of the serverside AJAX file to use; default = jqueryFileTree.php
//           folderEvent    - event to trigger expand/collapse; default = click
//           expandSpeed    - default = 500 (ms); use -1 for no animation
//           collapseSpeed  - default = 500 (ms); use -1 for no animation
//           expandEasing   - easing function to use on expand (optional)
//           collapseEasing - easing function to use on collapse (optional)
//           multiFolder    - whether or not to limit the browser to one subfolder at a time
//           loadMessage    - Message to display while initial tree loads (can be HTML)
//
// History:
//
// 1.01 - updated to work with foreign characters in directory/file names (12 April 2008)
// 1.00 - released (24 March 2008)
//
// TERMS OF USE
//
// This plugin is dual-licensed under the GNU General Public License and the MIT License and
// is copyright 2008 A Beautiful Site, LLC.
//
if(jQuery) (function($){

    $.extend($.fn, {
        fileTree: function(o, h) {
            // Defaults
            if( !o ) var o = {};
            if( o.root == undefined ) o.root = '/';
            if( o.script == undefined ) o.script = 'jqueryFileTree.php';
            if( o.folderEvent == undefined ) o.folderEvent = 'click';
            if( o.expandSpeed == undefined ) o.expandSpeed= 500;
            if( o.collapseSpeed == undefined ) o.collapseSpeed= 500;
            if( o.expandEasing == undefined ) o.expandEasing = null;
            if( o.collapseEasing == undefined ) o.collapseEasing = null;
            if( o.multiFolder == undefined ) o.multiFolder = true;
            if( o.loadMessage == undefined ) o.loadMessage = 'Loading...';

            $(this).each( function() {

                function showTree(c, t) {
                    $(c).addClass('wait');
                    $(".jqueryFileTree.start").remove();
                    $.get(o.script, { dir: t }, function(data) {
                        $(c).find('.start').html('');
                        $(c).removeClass('wait').append(data);
                        if ( o.root == t ) {
                            $(c).find('UL:hidden').show();
                        }
                        else {
                            $(c).find('UL:hidden').slideDown({ duration: o.expandSpeed, easing: o.expandEasing });
                        }
                        bindTree(c);
                    });
                }

                function bindTree(t) {
                    $(t).find('LI A').bind(o.folderEvent, function(e) {
                        if( $(this).parent().hasClass('directory') ) {
                            if ( e.ctrlKey ) {
                                $(this).parent().toggleClass('selected_file');
                                h($(this).attr('rel'));
                            }
                            else {
                                if( $(this).parent().hasClass('collapsed') ) {
                                    // Expand
                                    if( !o.multiFolder ) {
                                        $(this).parent().parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing });
                                        $(this).parent().parent().find('LI.directory').removeClass('expanded').addClass('collapsed');
                                    }
                                    $(this).parent().find('UL').remove(); // cleanup
                                    showTree( $(this).parent(), escape($(this).attr('rel').match( /.*\// )) );
                                    $(this).parent().removeClass('collapsed').addClass('expanded');
                                } else {
                                    // Collapse
                                    $(this).parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing });
                                    $(this).parent().removeClass('expanded').addClass('collapsed');
                                }
                            }
                        } else {
                            if ( e.shiftKey ) {
                                var parent = $(this).parent();
                                if ( parent.hasClass('selected_file') ) {
                                    var selector = ':not(.selected_file)';
                                } else {
                                    var selector = '.selected_file';
                                }

                                var nearest = $(this).parent().siblings(selector);
                                if ( nearest ) {
                                    if ( nearest.index() > parent.index() ) {
                                        parent.nextUntil(selector).toggleClass('selected_file');
                                    } else {
                                        parent.prevUntil(selector).toggleClass('selected_file');
                                    }
                                }
                            }
                            $(this).parent().toggleClass('selected_file');
                            h($(this).attr('rel'));
                        }
                        return false;
                    });
                    // Prevent A from triggering the # on non-click events
                    if ( o.folderEvent.toLowerCase != 'click' ) {
                        $(t).find('LI A').bind('click', function() {
                            return false;
                        });
                    }
                }
                // Loading message
                $(this).html('

' + o.loadMessage + '

');
                // Get the initial file list
                showTree( $(this), escape(o.root) );
            });
        }
    });

})(jQuery);
<commit_msg>Fix ctrl-click for Mac.  Closes #41.<commit_after>// jQuery File Tree Plugin
//
// Version 1.01
//
// Cory S.N. LaViska
// A Beautiful Site (http://abeautifulsite.net/)
// 24 March 2008
//
// Visit http://abeautifulsite.net/notebook.php?article=58 for more information
//
// Usage: $('.fileTreeDemo').fileTree( options, callback )
//
// Options:  root           - root folder to display; default = /
//           script         - location of the serverside AJAX file to use; default = jqueryFileTree.php
//           folderEvent    - event to trigger expand/collapse; default = click
//           expandSpeed    - default = 500 (ms); use -1 for no animation
//           collapseSpeed  - default = 500 (ms); use -1 for no animation
//           expandEasing   - easing function to use on expand (optional)
//           collapseEasing - easing function to use on collapse (optional)
//           multiFolder    - whether or not to limit the browser to one subfolder at a time
//           loadMessage    - Message to display while initial tree loads (can be HTML)
//
// History:
//
// 1.01 - updated to work with foreign characters in directory/file names (12 April 2008)
// 1.00 - released (24 March 2008)
//
// TERMS OF USE
//
// This plugin is dual-licensed under the GNU General Public License and the MIT License and
// is copyright 2008 A Beautiful Site, LLC.
//
if(jQuery) (function($){

    $.extend($.fn, {
        fileTree: function(o, h) {
            // Defaults
            if( !o ) var o = {};
            if( o.root == undefined ) o.root = '/';
            if( o.script == undefined ) o.script = 'jqueryFileTree.php';
            if( o.folderEvent == undefined ) o.folderEvent = 'click';
            if( o.expandSpeed == undefined ) o.expandSpeed= 500;
            if( o.collapseSpeed == undefined ) o.collapseSpeed= 500;
            if( o.expandEasing == undefined ) o.expandEasing = null;
            if( o.collapseEasing == undefined ) o.collapseEasing = null;
            if( o.multiFolder == undefined ) o.multiFolder = true;
            if( o.loadMessage == undefined ) o.loadMessage = 'Loading...';

            $(this).each( function() {

                function showTree(c, t) {
                    $(c).addClass('wait');
                    $(".jqueryFileTree.start").remove();
                    $.get(o.script, { dir: t }, function(data) {
                        $(c).find('.start').html('');
                        $(c).removeClass('wait').append(data);
                        if ( o.root == t ) {
                            $(c).find('UL:hidden').show();
                        }
                        else {
                            $(c).find('UL:hidden').slideDown({ duration: o.expandSpeed, easing: o.expandEasing });
                        }
                        bindTree(c);
                    });
                }

                function bindTree(t) {
                    $(t).find('LI A').bind(o.folderEvent, function(e) {
                        if( $(this).parent().hasClass('directory') ) {
                            if ( e.metaKey ) {
                                $(this).parent().toggleClass('selected_file');
                                h($(this).attr('rel'));
                            }
                            else {
                                if( $(this).parent().hasClass('collapsed') ) {
                                    // Expand
                                    if( !o.multiFolder ) {
                                        $(this).parent().parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing });
                                        $(this).parent().parent().find('LI.directory').removeClass('expanded').addClass('collapsed');
                                    }
                                    $(this).parent().find('UL').remove(); // cleanup
                                    showTree( $(this).parent(), escape($(this).attr('rel').match( /.*\// )) );
                                    $(this).parent().removeClass('collapsed').addClass('expanded');
                                } else {
                                    // Collapse
                                    $(this).parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing });
                                    $(this).parent().removeClass('expanded').addClass('collapsed');
                                }
                            }
                        } else {
                            if ( e.shiftKey ) {
                                var parent = $(this).parent();
                                if ( parent.hasClass('selected_file') ) {
                                    var selector = ':not(.selected_file)';
                                } else {
                                    var selector = '.selected_file';
                                }

                                var nearest = $(this).parent().siblings(selector);
                                if ( nearest ) {
                                    if ( nearest.index() > parent.index() ) {
                                        parent.nextUntil(selector).toggleClass('selected_file');
                                    } else {
                                        parent.prevUntil(selector).toggleClass('selected_file');
                                    }
                                }
                            }
                            $(this).parent().toggleClass('selected_file');
                            h($(this).attr('rel'));
                        }
                        return false;
                    });
                    // Prevent A from triggering the # on non-click events
                    if ( o.folderEvent.toLowerCase != 'click' ) {
                        $(t).find('LI A').bind('click', function() {
                            return false;
                        });
                    }
                }
                // Loading message
                $(this).html('

' + o.loadMessage + '

');
                // Get the initial file list
                showTree( $(this), escape(o.root) );
            });
        }
    });

})(jQuery);
<|endoftext|>


<commit_before>import React from 'react'
import {Link} from 'react-router-dom'
import FloatingActionButton from 'material-ui/FloatingActionButton'
import {blueA400, grey500, blueA100, red900 } from 'material-ui/styles/colors'
import { routerActions } from 'react-router-redux'
import { connect } from 'react-redux'
import { addFilter, removeFilter, removeResource } from '../state/actions/workloads'
import sizeMe from 'react-sizeme'
import FilterTable from './filter-table/FilterTable'
import * as moment from 'moment'

import ChipInput from 'material-ui-chip-input'
import Chip from 'material-ui/Chip'
import { withRouter } from 'react-router-dom'
import { linkForResource } from '../routes'
import IconAdd from 'material-ui/svg-icons/content/add'
import IconLogs from 'material-ui/svg-icons/action/receipt'
import IconShell from 'material-ui/svg-icons/hardware/computer'
import IconEdit from 'material-ui/svg-icons/editor/mode-edit'
import IconDelete from 'material-ui/svg-icons/action/delete'

import IconMore from 'material-ui/svg-icons/navigation/more-horiz'
import Popover from 'material-ui/Popover'
import Paper from 'material-ui/Paper'

import { arraysEqual } from '../comparators'
import { resourceStatus as resourceStatusIcons } from './icons'
import { compareStatuses } from '../resource-utils'

import KubeKinds from '../kube-kinds'
import './WorkloadsPage.css'

import Perf from 'react-addons-perf'

const mapStateToProps = function(store) {
  return {
    filters: store.workloads.filters,
    filterNames: store.workloads.filterNames,
    possibleFilters: store.workloads.possibleFilters,
  };
}

const mapDispatchToProps = function(dispatch, ownProps) {
  return {
    addFilter: function(filterName) {
      dispatch(addFilter(filterName))
    },
    removeFilter: function(filterName, index) {
      dispatch(removeFilter(filterName, index))
    },
    viewResource: function(resource, view='configuration') {
      dispatch(routerActions.push(linkForResource(resource,view)))
    },
    removeResource: function(...resources) {
      dispatch(removeResource(...resources))
    }
  }
}

const styles = {
  newResourceButton: {
    margin: 0,
    top: 100,
    right: 60,
    bottom: 'auto',
    left: 'auto',
    position: 'fixed',
  },
  deleteResourceButton: {
    margin: 0,
    top: 110,
    right: 130,
    bottom: 'auto',
    left: 'auto',
    position: 'fixed',
  },
  editButton: {
    fill: grey500
  },
  cell: {
    paddingLeft: 2,
    paddingRight: 2,
  },
  header: {
    fontWeight: 600,
    fontSize: '13px',
  },
  iconButton: {
    float: 'left',
    paddingTop: 17
  },
  editLink: {
    color: blueA400,
    fontWeight: 600,
    textDecoration: 'none',
  },
  miniButton: {
    margin: 10,
  },
  popover: {
    marginTop: -55,
    marginLeft: 0,
    paddingLeft: 15,
    paddingRight: 15,
    backgroundColor: '#BBB',
    border: '1px solid #000',
    borderRadius: '3px',
    boxShadow: 'rgba(0, 0, 0, 0.16) 0px 3px 10px, rgba(0, 0, 0, 0.23) 0px 3px 10px',
  },
  paper: {
    padding: 15,
    margin: 5,
  }
}

// use functional component style for representational components
export default sizeMe({ monitorWidth: true }) (
withRouter(connect(mapStateToProps, mapDispatchToProps) (
class WorkloadsPage extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      actionsOpen: false,
      hoveredRow: -1,
      hoveredResources: null,
    }
    this.selectedIds = {}
    this.rows = this.resourcesToRows(props.resources)
    this.columns = [
      {
        id: 'kind',
        label: 'kind',
        sortable: true,
        headerStyle: styles.header,
        style: { ...styles.cell,
          width: '60px',
        }
      },
      {
        id: 'name',
        label: 'name',
        sortable: true,
        headerStyle: styles.header,
        style: { ...styles.cell,
          width: '35%',
        },
      },
      {
        id: 'namespace',
        label: 'ns',
        sortable: true,
        headerStyle: styles.header,
        style: { ...styles.cell,
          width: 100,
        }
      },
      {
        id: 'status',
        label: 'status',
        headerStyle: styles.header,
        style: { ...styles.cell,
          width: 48,
          verticalAlign: 'middle',
        },
        sortable: true,
        comparator: compareStatuses,
      },
      {
        id: 'age',
        label: 'age',
        sortable: true,
        headerStyle: styles.header,
        style: { ...styles.cell,
          width: 100,
        }
      },
      {
        id: 'actions',
        label: 'actions ',
        headerStyle: styles.header,
        style: { ...styles.cell,
          width: '34px',
        },
        className: 'resource-actions',
      },
    ]
  }

  resourcesToRows = (resources) => {
    return Object.values(resources).filter(el => !el.isFiltered)
  }

  shouldComponentUpdate = (nextProps, nextState) => {
    return !arraysEqual(this.props.filterNames, nextProps.filterNames)
      || !arraysEqual(this.props.possibleFilters, nextProps.possibleFilters)
      || this.state.actionsOpen !== nextState.actionsOpen
      || this.state.hoveredRow !== nextState.hoveredRow
      || this.props.resources !== nextProps.resources
  }

  handleActionsRequestClose = () => {
    this.setState({
      actionsOpen: false,
      hoveredRow: -1,
      hoveredResource: null,
    })
  }

  handleRowSelection = (selectedIds) => {
    if (!this.actionsClicked) {
      this.selectedIds = selectedIds
      this.deleteButton.setDisabled(Object.keys(selectedIds).length === 0)
    }
  }

  handleCellClick = (rowId, colId, resource, col) => {
    this.actionsClicked = false
    if (col.id === 'actions') {
      let trs = document.getElementsByClassName('workloads filter-table')[1].children[0].children
      this.setState({
        actionsOpen: true,
        actionsAnchor: trs[rowId].children[colId+1],
        hoveredRow: rowId,
        hoveredResource: resource,
      })
      this.actionsClicked = true
      return false
    } else {
      this.props.viewResource(resource)
      return false
    }
  }

  handleDelete = () => {
    if (this.selectedIds && Object.keys(this.selectedIds).length > 0) {
      let resources = []
      for (let id in this.selectedIds) {
        resources.push(this.props.resources[id])
      }
      this.props.removeResource(...resources)
      this.handleRowSelection({})
    }
  }

  componentWillUpdate = () => {
    setTimeout(() => {
      Perf.start()
    }, 0)
  }

  componentDidUpdate = () => {
    Perf.stop()
    let m = Perf.getLastMeasurements()
    Perf.printWasted(m)
  }

  componentWillReceiveProps = (nextProps) => {
    this.rows = this.resourcesToRows(nextProps.resources)
  }

  renderCell = (column, row) => {
    switch(column) {
      case 'name':
        return row.metadata.name
      case 'namespace':
        return row.metadata.namespace
      case 'kind':
        return row.kind
      case 'actions':
        return <IconMore color={'rgba(0,0,0,0.4)'} hoverColor={'rgba(0,0,0,0.87)'} data-rh="Actions..."/>
      case 'age':
        let age = Date.now() - Date.parse(row.metadata.creationTimestamp)
        return moment.duration(age).humanize()
      case 'status':
        return resourceStatusIcons[row.statusSummary]
      default:
        return ''
    }
  }

  getCellValue = (column, row) => {
    switch(column) {
      case 'name':
        return row.metadata.name
      case 'namespace':
        return row.metadata.namespace
      case 'kind':
        return row.kind
      case 'age':
        return row.metadata.creationTimestamp
      case 'status':
        return row.statusSummary
      default:
        return ''
    }
  }

  render() {
    let { props } = this

    return (

      <Paper style={styles.paper}>

        {renderFilters(props)}

        <FilterTable
          className={'workloads'}
          columns={this.columns}
          data={this.rows}
          height={`${window.innerHeight - 290}px`}
          multiSelectable={true}
          onRowSelection={this.handleRowSelection.bind(this)}
          onCellClick={this.handleCellClick.bind(this)}
          hoveredRow={this.state.hoveredRow}
          onRenderCell={this.renderCell}
          getCellValue={this.getCellValue}
          selectedIds={this.selectedIds}
          stripedRows={false}
          width={'calc(100vw - 110px)'}
          />

        {this.state.hoveredResource &&
        <Popover
          className="actions-popover"
          style={styles.popover}
          open={this.state.actionsOpen}
          anchorEl={this.state.actionsAnchor}
          onRequestClose={this.handleActionsRequestClose}
          zDepth={0}
          anchorOrigin={{horizontal: 'left', vertical: 'bottom'}}
          targetOrigin={{horizontal: 'left', vertical: 'top'}}
        >
          {KubeKinds.workloads[this.state.hoveredResource.kind].hasLogs &&
          <FloatingActionButton mini={true} style={styles.miniButton}
            onTouchTap={()=> { this.props.viewResource(this.state.hoveredResource,'logs') }}
            data-rh="View Logs...">
            <IconLogs/>
          </FloatingActionButton>
          }

          {KubeKinds.workloads[this.state.hoveredResource.kind].hasTerminal &&
          <FloatingActionButton mini={true} style={styles.miniButton}
            onTouchTap={()=> { this.props.viewResource(this.state.hoveredResource,'terminal') }}
            data-rh="Open Terminal...">
            <IconShell/>
          </FloatingActionButton>
          }

          {/* TODO: need to check whether this resource can actually be edited by the user */}
          <FloatingActionButton mini={true} style={styles.miniButton}
            onTouchTap={()=> { this.props.viewResource(this.state.hoveredResource,'edit') }}
            data-rh="Edit...">
            <IconEdit/>
          </FloatingActionButton >
        </Popover>
        }
        <Link to="/workloads/new" >
          <FloatingActionButton style={styles.newResourceButton} backgroundColor={blueA400}>
            <IconAdd />
          </FloatingActionButton>
        </Link>

        <DeleteButton backgroundColor={red900}
          mini={true}
          style={styles.deleteResourceButton}
          disabled={Object.keys(this.selectedIds).length === 0}
          onTouchTap={this.handleDelete}
          ref={(ref)=>{this.deleteButton = ref}}/>

      </Paper>

    )
  }
})))

class DeleteButton extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      disabled: props.disabled,
    }
  }

  setDisabled = (disabled) => {
    this.setState({disabled: disabled})
  }

  render() {
    let { props } = this
    return <FloatingActionButton {...props} disabled={this.state.disabled}>
       <IconDelete/>
      </FloatingActionButton>
  }

}

function renderFilters(props) {

  return <ChipInput
    value={props.filterNames}
    onRequestAdd={(filter) => props.addFilter(filter)}
    onRequestDelete={(filter, index) => props.removeFilter(filter, index)}
    name={'filters'}
    dataSource={props.possibleFilters}
    floatingLabelText={'select by filters...'}
    defaultValue={['namespace:default']}
    menuProps={{
      desktop: true,
    }}
    chipRenderer={({ value, isFocused, isDisabled, handleClick, handleRequestDelete }, key) => {

      var labelText = value;
      var parts=value.split(":")
      if (parts.length === 2) {
        labelText={parts[0]}:{parts[1]}
      } else if (parts.length === 1) {
        labelText=*:{parts[0]}
      }
      return (
        <Chip
          key={key}
          style={{
            margin: '8px 8px 0 0',
            padding: 0,
            float: 'left',
            pointerEvents: isDisabled ? 'none' : undefined
          }}
          labelStyle={{'lineHeight': '30px'}}
          backgroundColor={isFocused ? blueA100 : null}
          onTouchTap={handleClick}
          onRequestDelete={handleRequestDelete}
        >
        {labelText}
        </Chip>
      )}
    }
    underlineShow={true}
    fullWidth={true}
    style={styles.textField}
    inputStyle={styles.inputStyle}
    hintStyle={styles.hintStyle}
  />
}<commit_msg>wider kind field<commit_after>import React from 'react'
import {Link} from 'react-router-dom'
import FloatingActionButton from 'material-ui/FloatingActionButton'
import {blueA400, grey500, blueA100, red900 } from 'material-ui/styles/colors'
import { routerActions } from 'react-router-redux'
import { connect } from 'react-redux'
import { addFilter, removeFilter, removeResource } from '../state/actions/workloads'
import sizeMe from 'react-sizeme'
import FilterTable from './filter-table/FilterTable'
import * as moment from 'moment'

import ChipInput from 'material-ui-chip-input'
import Chip from 'material-ui/Chip'
import { withRouter } from 'react-router-dom'
import { linkForResource } from '../routes'
import IconAdd from 'material-ui/svg-icons/content/add'
import IconLogs from 'material-ui/svg-icons/action/receipt'
import IconShell from 'material-ui/svg-icons/hardware/computer'
import IconEdit from 'material-ui/svg-icons/editor/mode-edit'
import IconDelete from 'material-ui/svg-icons/action/delete'

import IconMore from 'material-ui/svg-icons/navigation/more-horiz'
import Popover from 'material-ui/Popover'
import Paper from 'material-ui/Paper'

import { arraysEqual } from '../comparators'
import { resourceStatus as resourceStatusIcons } from './icons'
import { compareStatuses } from '../resource-utils'

import KubeKinds from '../kube-kinds'
import './WorkloadsPage.css'

import Perf from 'react-addons-perf'

const mapStateToProps = function(store) {
  return {
    filters: store.workloads.filters,
    filterNames: store.workloads.filterNames,
    possibleFilters: store.workloads.possibleFilters,
  };
}

const mapDispatchToProps = function(dispatch, ownProps) {
  return {
    addFilter: function(filterName) {
      dispatch(addFilter(filterName))
    },
    removeFilter: function(filterName, index) {
      dispatch(removeFilter(filterName, index))
    },
    viewResource: function(resource, view='configuration') {
      dispatch(routerActions.push(linkForResource(resource,view)))
    },
    removeResource: function(...resources) {
      dispatch(removeResource(...resources))
    }
  }
}

const styles = {
  newResourceButton: {
    margin: 0,
    top: 100,
    right: 60,
    bottom: 'auto',
    left: 'auto',
    position: 'fixed',
  },
  deleteResourceButton: {
    margin: 0,
    top: 110,
    right: 130,
    bottom: 'auto',
    left: 'auto',
    position: 'fixed',
  },
  editButton: {
    fill: grey500
  },
  cell: {
    paddingLeft: 2,
    paddingRight: 2,
  },
  header: {
    fontWeight: 600,
    fontSize: '13px',
  },
  iconButton: {
    float: 'left',
    paddingTop: 17
  },
  editLink: {
    color: blueA400,
    fontWeight: 600,
    textDecoration: 'none',
  },
  miniButton: {
    margin: 10,
  },
  popover: {
    marginTop: -55,
    marginLeft: 0,
    paddingLeft: 15,
    paddingRight: 15,
    backgroundColor: '#BBB',
    border: '1px solid #000',
    borderRadius: '3px',
    boxShadow: 'rgba(0, 0, 0, 0.16) 0px 3px 10px, rgba(0, 0, 0, 0.23) 0px 3px 10px',
  },
  paper: {
    padding: 15,
    margin: 5,
  }
}

// use functional component style for representational components
export default sizeMe({ monitorWidth: true }) (
withRouter(connect(mapStateToProps, mapDispatchToProps) (
class WorkloadsPage extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      actionsOpen: false,
      hoveredRow: -1,
      hoveredResources: null,
    }
    this.selectedIds = {}
    this.rows = this.resourcesToRows(props.resources)
    this.columns = [
      {
        id: 'kind',
        label: 'kind',
        sortable: true,
        headerStyle: styles.header,
        style: { ...styles.cell,
          width: '80px',
        }
      },
      {
        id: 'name',
        label: 'name',
        sortable: true,
        headerStyle: styles.header,
        style: { ...styles.cell,
          width: '35%',
        },
      },
      {
        id: 'namespace',
        label: 'ns',
        sortable: true,
        headerStyle: styles.header,
        style: { ...styles.cell,
          width: 100,
        }
      },
      {
        id: 'status',
        label: 'status',
        headerStyle: styles.header,
        style: { ...styles.cell,
          width: 48,
          verticalAlign: 'middle',
        },
        sortable: true,
        comparator: compareStatuses,
      },
      {
        id: 'age',
        label: 'age',
        sortable: true,
        headerStyle: styles.header,
        style: { ...styles.cell,
          width: 100,
        }
      },
      {
        id: 'actions',
        label: 'actions ',
        headerStyle: styles.header,
        style: { ...styles.cell,
          width: '34px',
        },
        className: 'resource-actions',
      },
    ]
  }

  resourcesToRows = (resources) => {
    return Object.values(resources).filter(el => !el.isFiltered)
  }

  shouldComponentUpdate = (nextProps, nextState) => {
    return !arraysEqual(this.props.filterNames, nextProps.filterNames)
      || !arraysEqual(this.props.possibleFilters, nextProps.possibleFilters)
      || this.state.actionsOpen !== nextState.actionsOpen
      || this.state.hoveredRow !== nextState.hoveredRow
      || this.props.resources !== nextProps.resources
  }

  handleActionsRequestClose = () => {
    this.setState({
      actionsOpen: false,
      hoveredRow: -1,
      hoveredResource: null,
    })
  }

  handleRowSelection = (selectedIds) => {
    if (!this.actionsClicked) {
      this.selectedIds = selectedIds
      this.deleteButton.setDisabled(Object.keys(selectedIds).length === 0)
    }
  }

  handleCellClick = (rowId, colId, resource, col) => {
    this.actionsClicked = false
    if (col.id === 'actions') {
      let trs = document.getElementsByClassName('workloads filter-table')[1].children[0].children
      this.setState({
        actionsOpen: true,
        actionsAnchor: trs[rowId].children[colId+1],
        hoveredRow: rowId,
        hoveredResource: resource,
      })
      this.actionsClicked = true
      return false
    } else {
      this.props.viewResource(resource)
      return false
    }
  }

  handleDelete = () => {
    if (this.selectedIds && Object.keys(this.selectedIds).length > 0) {
      let resources = []
      for (let id in this.selectedIds) {
        resources.push(this.props.resources[id])
      }
      this.props.removeResource(...resources)
      this.handleRowSelection({})
    }
  }

  componentWillUpdate = () => {
    setTimeout(() => {
      Perf.start()
    }, 0)
  }

  componentDidUpdate = () => {
    Perf.stop()
    let m = Perf.getLastMeasurements()
    Perf.printWasted(m)
  }

  componentWillReceiveProps = (nextProps) => {
    this.rows = this.resourcesToRows(nextProps.resources)
  }

  renderCell = (column, row) => {
    switch(column) {
      case 'name':
        return row.metadata.name
      case 'namespace':
        return row.metadata.namespace
      case 'kind':
        return row.kind
      case 'actions':
        return <IconMore color={'rgba(0,0,0,0.4)'} hoverColor={'rgba(0,0,0,0.87)'} data-rh="Actions..."/>
      case 'age':
        let age = Date.now() - Date.parse(row.metadata.creationTimestamp)
        return moment.duration(age).humanize()
      case 'status':
        return resourceStatusIcons[row.statusSummary]
      default:
        return ''
    }
  }

  getCellValue = (column, row) => {
    switch(column) {
      case 'name':
        return row.metadata.name
      case 'namespace':
        return row.metadata.namespace
      case 'kind':
        return row.kind
      case 'age':
        return row.metadata.creationTimestamp
      case 'status':
        return row.statusSummary
      default:
        return ''
    }
  }

  render() {
    let { props } = this

    return (

      <Paper style={styles.paper}>

        {renderFilters(props)}

        <FilterTable
          className={'workloads'}
          columns={this.columns}
          data={this.rows}
          height={`${window.innerHeight - 290}px`}
          multiSelectable={true}
          onRowSelection={this.handleRowSelection.bind(this)}
          onCellClick={this.handleCellClick.bind(this)}
          hoveredRow={this.state.hoveredRow}
          onRenderCell={this.renderCell}
          getCellValue={this.getCellValue}
          selectedIds={this.selectedIds}
          stripedRows={false}
          width={'calc(100vw - 110px)'}
          />

        {this.state.hoveredResource &&
        <Popover
          className="actions-popover"
          style={styles.popover}
          open={this.state.actionsOpen}
          anchorEl={this.state.actionsAnchor}
          onRequestClose={this.handleActionsRequestClose}
          zDepth={0}
          anchorOrigin={{horizontal: 'left', vertical: 'bottom'}}
          targetOrigin={{horizontal: 'left', vertical: 'top'}}
        >
          {KubeKinds.workloads[this.state.hoveredResource.kind].hasLogs &&
          <FloatingActionButton mini={true} style={styles.miniButton}
            onTouchTap={()=> { this.props.viewResource(this.state.hoveredResource,'logs') }}
            data-rh="View Logs...">
            <IconLogs/>
          </FloatingActionButton>
          }

          {KubeKinds.workloads[this.state.hoveredResource.kind].hasTerminal &&
          <FloatingActionButton mini={true} style={styles.miniButton}
            onTouchTap={()=> { this.props.viewResource(this.state.hoveredResource,'terminal') }}
            data-rh="Open Terminal...">
            <IconShell/>
          </FloatingActionButton>
          }

          {/* TODO: need to check whether this resource can actually be edited by the user */}
          <FloatingActionButton mini={true} style={styles.miniButton}
            onTouchTap={()=> { this.props.viewResource(this.state.hoveredResource,'edit') }}
            data-rh="Edit...">
            <IconEdit/>
          </FloatingActionButton >
        </Popover>
        }
        <Link to="/workloads/new" >
          <FloatingActionButton style={styles.newResourceButton} backgroundColor={blueA400}>
            <IconAdd />
          </FloatingActionButton>
        </Link>

        <DeleteButton backgroundColor={red900}
          mini={true}
          style={styles.deleteResourceButton}
          disabled={Object.keys(this.selectedIds).length === 0}
          onTouchTap={this.handleDelete}
          ref={(ref)=>{this.deleteButton = ref}}/>

      </Paper>

    )
  }
})))

class DeleteButton extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      disabled: props.disabled,
    }
  }

  setDisabled = (disabled) => {
    this.setState({disabled: disabled})
  }

  render() {
    let { props } = this
    return <FloatingActionButton {...props} disabled={this.state.disabled}>
       <IconDelete/>
      </FloatingActionButton>
  }

}

function renderFilters(props) {

  return <ChipInput
    value={props.filterNames}
    onRequestAdd={(filter) => props.addFilter(filter)}
    onRequestDelete={(filter, index) => props.removeFilter(filter, index)}
    name={'filters'}
    dataSource={props.possibleFilters}
    floatingLabelText={'select by filters...'}
    defaultValue={['namespace:default']}
    menuProps={{
      desktop: true,
    }}
    chipRenderer={({ value, isFocused, isDisabled, handleClick, handleRequestDelete }, key) => {

      var labelText = value;
      var parts=value.split(":")
      if (parts.length === 2) {
        labelText={parts[0]}:{parts[1]}
      } else if (parts.length === 1) {
        labelText=*:{parts[0]}
      }
      return (
        <Chip
          key={key}
          style={{
            margin: '8px 8px 0 0',
            padding: 0,
            float: 'left',
            pointerEvents: isDisabled ? 'none' : undefined
          }}
          labelStyle={{'lineHeight': '30px'}}
          backgroundColor={isFocused ? blueA100 : null}
          onTouchTap={handleClick}
          onRequestDelete={handleRequestDelete}
        >
        {labelText}
        </Chip>
      )}
    }
    underlineShow={true}
    fullWidth={true}
    style={styles.textField}
    inputStyle={styles.inputStyle}
    hintStyle={styles.hintStyle}
  />
}<|endoftext|>


<commit_before>import React, {PropTypes, Component} from 'react'
import {formatRPDuration} from 'utils/formatting'
import YesNoButtons from 'src/shared/components/YesNoButtons'
import onClickOutside from 'react-onclickoutside'

class DatabaseRow extends Component {
  constructor(props) {
    super(props)
    this.state = {
      isEditing: false,
      isDeleting: false,
    }
    this.handleKeyDown = ::this.handleKeyDown
    this.handleClickOutside = ::this.handleClickOutside
    this.handleStartEdit = ::this.handleStartEdit
    this.handleEndEdit = ::this.handleEndEdit
    this.handleCreate = ::this.handleCreate
    this.handleUpdate = ::this.handleUpdate
    this.getInputValues = ::this.getInputValues
    this.handleStartDelete = ::this.handleStartDelete
    this.handleEndDelete = ::this.handleEndDelete
  }

  componentWillMount() {
    if (this.props.retentionPolicy.isNew) {
      this.setState({isEditing: true})
    }
  }

  render() {
    const {
      onRemove,
      retentionPolicy: {name, duration, replication, isDefault, isNew},
      retentionPolicy,
      database,
      onDelete,
      isRFDisplayed,
    } = this.props
    const {isEditing, isDeleting} = this.state

    const formattedDuration = formatRPDuration(duration)

    if (isEditing) {
      return (

          {
            isNew ?

                <input
                  className="form-control"
                  type="text"
                  defaultValue={name}
                  placeholder="give it a name"
                  onKeyDown={(e) => this.handleKeyDown(e, database)}
                  ref={(r) => this.name = r}
                  autoFocus={true}
                />

 :

                {name}

          }

              <input
                className="form-control"
                name="name"
                type="text"
                defaultValue={formattedDuration}
                placeholder="how long should data last"
                onKeyDown={(e) => this.handleKeyDown(e, database)}
                ref={(r) => this.duration = r}
                autoFocus={!isNew}
              />

              <input
                className="form-control"
                name="name"
                type="number"
                min="1"
                defaultValue={replication || 1}
                placeholder="how many nodes do you have"
                onKeyDown={(e) => this.handleKeyDown(e, database)}
                ref={(r) => this.replication = r}
              />

            <YesNoButtons
              onConfirm={isNew ? this.handleCreate : this.handleUpdate}
              onCancel={isNew ? () => onRemove(database, retentionPolicy) : this.handleEndEdit}
            />

      )
    }

    return (

        {name} {isDefault ? default : null}
        {formattedDuration}
        {isRFDisplayed ? {replication}	 : null}

          {
            isDeleting ?
              <YesNoButtons onConfirm={() => onDelete(database, retentionPolicy)} onCancel={this.handleEndDelete} /> :
              this.renderDeleteButton()
          }

    )
  }

  renderDeleteButton() {
    if (!this.props.isDeletable) {
      return
    }

    return (
      <button className="btn btn-xs btn-danger admin-table--delete" onClick={this.handleStartDelete}>
        {`Delete ${name}`}
      </button>
    )
  }

  handleClickOutside() {
    const {database, retentionPolicy, onRemove} = this.props
    if (retentionPolicy.isNew) {
      onRemove(database, retentionPolicy)
    }

    this.handleEndEdit()
    this.handleEndDelete()
  }

  handleStartEdit() {
    this.setState({isEditing: true})
  }

  handleEndEdit() {
    this.setState({isEditing: false})
  }

  handleStartDelete() {
    this.setState({isDeleting: true})
  }

  handleEndDelete() {
    this.setState({isDeleting: false})
  }

  handleCreate() {
    const {database, retentionPolicy, onCreate} = this.props
    const validInputs = this.getInputValues()
    if (!validInputs) {
      return
    }

    onCreate(database, {...retentionPolicy, ...validInputs})
    this.handleEndEdit()
  }

  handleUpdate() {
    const {database, retentionPolicy, onUpdate} = this.props
    const validInputs = this.getInputValues()
    if (!validInputs) {
      return
    }

    onUpdate(database, retentionPolicy, validInputs)
    this.handleEndEdit()
  }

  handleKeyDown(e) {
    const {key} = e
    const {retentionPolicy, database, onRemove} = this.props

    if (key === 'Escape') {
      if (retentionPolicy.isNew) {
        onRemove(database, retentionPolicy)
        return
      }

      this.handleEndEdit()
    }

    if (key === 'Enter') {
      if (retentionPolicy.isNew) {
        this.handleCreate()
        return
      }

      this.handleUpdate()
    }
  }

  getInputValues() {
    let duration = this.duration.value.trim()
    const replication = +this.replication.value.trim()
    const {notify, retentionPolicy: {name}} = this.props

    if (!duration || !replication) {
      notify('error', 'Fields cannot be empty')
      return
    }

    if (duration === '∞') {
      duration = 'INF'
    }

    return {
      name,
      duration,
      replication,
    }
  }

}

const {
  bool,
  func,
  number,
  shape,
  string,
} = PropTypes

DatabaseRow.propTypes = {
  retentionPolicy: shape({
    name: string,
    duration: string,
    replication: number,
    isDefault: bool,
    isEditing: bool,
  }),
  isDeletable: bool,
  database: shape(),
  onRemove: func,
  onCreate: func,
  onUpdate: func,
  onDelete: func,
  notify: func,
  isRFDisplayed: bool,
}

export default onClickOutside(DatabaseRow)
<commit_msg>Fix DB manager table column sizing<commit_after>import React, {PropTypes, Component} from 'react'
import {formatRPDuration} from 'utils/formatting'
import YesNoButtons from 'src/shared/components/YesNoButtons'
import onClickOutside from 'react-onclickoutside'

class DatabaseRow extends Component {
  constructor(props) {
    super(props)
    this.state = {
      isEditing: false,
      isDeleting: false,
    }
    this.handleKeyDown = ::this.handleKeyDown
    this.handleClickOutside = ::this.handleClickOutside
    this.handleStartEdit = ::this.handleStartEdit
    this.handleEndEdit = ::this.handleEndEdit
    this.handleCreate = ::this.handleCreate
    this.handleUpdate = ::this.handleUpdate
    this.getInputValues = ::this.getInputValues
    this.handleStartDelete = ::this.handleStartDelete
    this.handleEndDelete = ::this.handleEndDelete
  }

  componentWillMount() {
    if (this.props.retentionPolicy.isNew) {
      this.setState({isEditing: true})
    }
  }

  render() {
    const {
      onRemove,
      retentionPolicy: {name, duration, replication, isDefault, isNew},
      retentionPolicy,
      database,
      onDelete,
      isDeletable,
      isRFDisplayed,
    } = this.props
    const {isEditing, isDeleting} = this.state

    const formattedDuration = formatRPDuration(duration)

    if (isEditing) {
      return (

          {
            isNew ?

                <input
                  className="form-control"
                  type="text"
                  defaultValue={name}
                  placeholder="give it a name"
                  onKeyDown={(e) => this.handleKeyDown(e, database)}
                  ref={(r) => this.name = r}
                  autoFocus={true}
                />

 :

                {name}

          }

              <input
                className="form-control"
                name="name"
                type="text"
                defaultValue={formattedDuration}
                placeholder="how long should data last"
                onKeyDown={(e) => this.handleKeyDown(e, database)}
                ref={(r) => this.duration = r}
                autoFocus={!isNew}
              />

              <input
                className="form-control"
                name="name"
                type="number"
                min="1"
                defaultValue={replication || 1}
                placeholder="how many nodes do you have"
                onKeyDown={(e) => this.handleKeyDown(e, database)}
                ref={(r) => this.replication = r}
              />

            <YesNoButtons
              onConfirm={isNew ? this.handleCreate : this.handleUpdate}
              onCancel={isNew ? () => onRemove(database, retentionPolicy) : this.handleEndEdit}
            />

      )
    }

    return (

        {name} {isDefault ? default : null}
        {formattedDuration}
        {isRFDisplayed ? {replication}	 : null}

          {
            isDeleting ?
              <YesNoButtons
                onConfirm={() => onDelete(database, retentionPolicy)}
                onCancel={this.handleEndDelete} /> :
              <button
                className="btn btn-xs btn-danger admin-table--delete"
                style={isDeletable ? {} : {visibility: 'hidden'}}
                onClick={this.handleStartDelete}>{`Delete ${name}`}
              </button>
          }

    )
  }

  handleClickOutside() {
    const {database, retentionPolicy, onRemove} = this.props
    if (retentionPolicy.isNew) {
      onRemove(database, retentionPolicy)
    }

    this.handleEndEdit()
    this.handleEndDelete()
  }

  handleStartEdit() {
    this.setState({isEditing: true})
  }

  handleEndEdit() {
    this.setState({isEditing: false})
  }

  handleStartDelete() {
    this.setState({isDeleting: true})
  }

  handleEndDelete() {
    this.setState({isDeleting: false})
  }

  handleCreate() {
    const {database, retentionPolicy, onCreate} = this.props
    const validInputs = this.getInputValues()
    if (!validInputs) {
      return
    }

    onCreate(database, {...retentionPolicy, ...validInputs})
    this.handleEndEdit()
  }

  handleUpdate() {
    const {database, retentionPolicy, onUpdate} = this.props
    const validInputs = this.getInputValues()
    if (!validInputs) {
      return
    }

    onUpdate(database, retentionPolicy, validInputs)
    this.handleEndEdit()
  }

  handleKeyDown(e) {
    const {key} = e
    const {retentionPolicy, database, onRemove} = this.props

    if (key === 'Escape') {
      if (retentionPolicy.isNew) {
        onRemove(database, retentionPolicy)
        return
      }

      this.handleEndEdit()
    }

    if (key === 'Enter') {
      if (retentionPolicy.isNew) {
        this.handleCreate()
        return
      }

      this.handleUpdate()
    }
  }

  getInputValues() {
    let duration = this.duration.value.trim()
    const replication = +this.replication.value.trim()
    const {notify, retentionPolicy: {name}} = this.props

    if (!duration || !replication) {
      notify('error', 'Fields cannot be empty')
      return
    }

    if (duration === '∞') {
      duration = 'INF'
    }

    return {
      name,
      duration,
      replication,
    }
  }

}

const {
  bool,
  func,
  number,
  shape,
  string,
} = PropTypes

DatabaseRow.propTypes = {
  retentionPolicy: shape({
    name: string,
    duration: string,
    replication: number,
    isDefault: bool,
    isEditing: bool,
  }),
  isDeletable: bool,
  database: shape(),
  onRemove: func,
  onCreate: func,
  onUpdate: func,
  onDelete: func,
  notify: func,
  isRFDisplayed: bool,
}

export default onClickOutside(DatabaseRow)
<|endoftext|>