MediaWiki:Gadget-QQ.js

From Wiktionary, the free dictionary
Jump to navigation Jump to search

Note – after saving, you may have to bypass your browser’s cache to see the changes.

  • Mozilla / Firefox / Safari: hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Command-R on a Macintosh);
  • Konqueror and Chrome: click Reload or press F5;
  • Opera: clear the cache in Tools → Preferences;
  • Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5.

Quiet Quentin (script) aids in searching Google Books for attesting quotations. To enable this gadget, tick "Quiet Quentin, a gadget assisting in finding citations" in the Gadgets tab of your preferences (or in Wiktionary:Preferences/V2.)

How to use: click the "QQ" tab at the top of the page (or press Alt-Shift-9), put in your search terms and click the search button. A list of results will then show up: to select one for inclusion, click the "[pick]" link on the left of the result. When you are done, click the "Picked citations" tab; you will be presented with wikicode you can copy and paste into the edit window. After carefully checking the citations for errors (like misscans, wrong or incomplete metadata), emboldening the term and ensuring that it is presented in proper context, save the page.


'use strict'; 
// {{documentation}} <nowiki>
/*jshint shadow:true, scripturl:true, undef:true, latedef:true, unused:true, loopfunc:true */
/*globals mw, jQuery */
function el(tag, child, attr, events) {
	var node = document.createElement(tag);
 
	if (child) {
		if (typeof child !== 'object')
			child = [child];
		for (var i = 0; i < child.length; ++i) {
			var ch = child[i];
			if ((ch === void(null)) || (ch === null))
				continue;
			else if (typeof ch !== 'object')
				ch = document.createTextNode(String(ch));
			node.appendChild(ch);
		}
	}
 
	if (attr) for (var key in attr) {
		node.setAttribute(key, String(attr[key]));
	}
 
	if (events) for (var key in events) {
		node.addEventListener(key, events[key], false);
	}
 
	return node;
}

var backends = {
	gbooks: {
		name: "Google Books",
		enabled: true,
		
		addResults: function (data, callback) {
			function makeResult(item) {
				var m;
				
				var texttype;
				if (item.volumeInfo.printType.includes('BOOK'))
					texttype = "book";
				else if (item.volumeInfo.printType.includes('MAGAZINE'))
					texttype = "journal"
				else
					texttype = "text";
				
				
				var lang;
				if (item.volumeInfo.language) {
					lang = item.volumeInfo.language.replace('iw', 'he'); // hardcoded exceptions can be added here
				}
				
				var year, month, day;
				if (item.volumeInfo.publishedDate) {
					if (m = /(\d{4})-?(\d{2})?-?(\d{2})?/.exec(item.volumeInfo.publishedDate)) {
						year = m[1];
						month = m[2];
						day = m[3];
					}
				}
				
				var page;
				if (item.volumeInfo.previewLink) {
					var uri = new mw.Uri(item.volumeInfo.previewLink);
					if (uri.query.pg) {
						if (m = /PA(\d+)/.exec(uri.query.pg))
							page = m[1];
					}
				}
				
				var idents = {};
				if (item.volumeInfo.industryIdentifiers) {
					for (var i = 0; i < item.volumeInfo.industryIdentifiers.length; ++i) {
						var ident = item.volumeInfo.industryIdentifiers[i];
						idents[ident.type] = ident.identifier;
					}
				}
				var isbn = idents.ISBN_13 || idents.ISBN_10;
				
				return {
					generateCitation: function () {
						var result = "* {{quote-";
						
						result += texttype;
						
						if (lang)
							result += "|" + lang;
						
						if (month && day)
							result += "|date=" + year + "-" + month + "-" + day;
						else if (month)
							result += "|year=" + year + "|month=" + month;
						else if (year)
							result += "|year=" + year;
						
						if (item.volumeInfo.authors) {
							result += "|author=" + item.volumeInfo.authors[0];
							
							if (item.volumeInfo.authors.length > 1) {
								for (var i = 1; i < item.volumeInfo.authors.length; i++) {
									var n = i + 1;
									result += "|author" + n + "=" + item.volumeInfo.authors[i];
								}
							}
						}

						if (item.volumeInfo.title) {
							result += "|title=" + item.volumeInfo.title;
							if (item.volumeInfo.subtitle)
								result += ": " + item.volumeInfo.subtitle;
						}
						
						if (item.volumeInfo.publisher)
							result += "|publisher=" + item.volumeInfo.publisher;
							
						if (isbn)
							result += "|isbn=" + isbn;
							
						if (page)
							result += "|page=" + page;
						
						if (item.searchInfo && item.searchInfo.textSnippet) {
							var text = item.searchInfo.textSnippet.replace(/<.*?>/g, '')
							.replace(/\n/g, " ")
							.replace(/&nbsp;\.\.\./g, "{{nb...}}")
							.replace(/&#39;/g, "'")
							.replace(/&quot;/g, "\"");
							result += "\n|text=" + text + "}}\n";
						} else {
							result += "}}\n";
						}

						return result;
					},
					
					populateNode: function (node, checkbox) {
						node.appendChild(el('label', [
							checkbox,
							year ? el('strong', year) : el('i', "date unknown"),
							item.volumeInfo.authors && ", ",
							item.volumeInfo.authors && item.volumeInfo.authors.join(", "),
							", ",
							el('cite', [
								item.volumeInfo.title,
								item.volumeInfo.subtitle && el('small', [": ", item.volumeInfo.subtitle])
							]),
						]));

						node.appendChild(el('span', [
							item.volumeInfo.publisher && el('span', [
								", ",
								item.volumeInfo.publisher,
							], { 'class': 'publisher' }),
							isbn && el('span', [
								" (",
								el('a', ["ISBN " + isbn], {
									'href': mw.util.getUrl('Special:BookSources/' + isbn)
								}),
								")"
							], { 'class': 'isbn' }),
							page && el('span', [
								", page ",
								page,
							], { 'class': 'page' }),
							
							item.volumeInfo.previewLink && el('small', [
								" [",
								el('a', "view", {
									'href': item.volumeInfo.previewLink
								}),
								"]"
							]),
							
							item.volumeInfo.infoLink && el('small', [
								" [",
								el('a', "info", {
									'href': item.volumeInfo.infoLink
								}),
								"]"
							]),
							
							el('small', [
								" [",
								el('a', "raw", {
									'href': 'javascript:void(0);'
								}, {
									'click': function () {
										alert(JSON.stringify(item, null, '  '));
									}
								}),
								"]"
							])
						]));
						
						if (item.searchInfo && item.searchInfo.textSnippet) {
							var uiQuote = el('blockquote');
							uiQuote.innerHTML = item.searchInfo.textSnippet.replace(/<br>/g, " ");
							node.appendChild(uiQuote);
						}
					}
				};
			}

			if (!data.items) {
				this.uiStatus.textContent = "no more results";
				this.uiStatus.style.display = '';
				return;
			}
			
			for (var i = 0; i < data.items.length; ++i) {
				if (data.items[i].searchInfo && data.items[i].searchInfo.textSnippet) {
					if (!/<b>/.test(data.items[i].searchInfo.textSnippet))
						continue; // bogus result
				}
				callback(makeResult(data.items[i]));
			}
			
			this.uiMore.style.display = '';
			this.uiStatus.textContent = "";
			this.data.startIndex += data.items.length;
		},
		
		populateTab: function (tab) {
			var that = this;

			this.uiResults = el('ul');
			this.uiStatus = el('div');
			this.uiMore = el('a', ["more results"], {
				'href': 'javascript:void(0);'
			}, {
				'click': function () {
					that.uiStatus.textContent = "please wait...";
					this.style.display = 'none';
					jQuery.ajax({
						url: location.protocol + '//www.googleapis.com/books/v1/volumes',
						dataType: 'jsonp',
						data: that.data
					}).done(function (data) {
						that.addResults(data, that.callback);
					});
				}
			});
			
			tab.appendChild(this.uiResults);
			tab.appendChild(this.uiStatus);
			tab.appendChild(this.uiMore);
		},

		search: function (terms, options, callback) {
			var that = this;
			while (this.uiResults.firstChild)
				this.uiResults.removeChild(this.uiResults.firstChild);

			this.data = {
				q: terms,
				startIndex: 0,
				maxResults: 20
				// orderBy: 'newest' // reverse chronological order; no option for plain chronological order, unforutnately
			};
			this.callback = callback;
			this.uiMore.style.display = 'none';
			this.uiStatus.textContent = "please wait...";
			jQuery.ajax({
				url: location.protocol + '//www.googleapis.com/books/v1/volumes',
				dataType: 'jsonp',
				data: this.data
			}).done(function (data) {
				that.addResults(data, callback);
			});
		}
	}
};

var uiQuery, uiTitle, uiBackends, uiTabs, uiTabLabels, uiCitationsTab, uiCitations, uiCurTab, uiCurTabLabel, uiForm;
var uiMain = el('div', [
	uiTitle = el('div', [
		el('strong', "Quiet Quentin"),
		el('a', "hide", {
			'href': 'javascript:void(0);',
			'class': 'hide-link'
		}, {
			'click': function () {
				uiMain.style.display = 'none';
			}
		})
	], { 'class': 'title' }),
	uiForm = el('form', [
		el('fieldset', [
			el('legend', "Search for attestations"),
			uiQuery = el('input', null, {
				'type': 'text',
				'placeholder': "term",
				'class': 'search-terms',
				'value': (function () {
					var m;
					if ([0, 1, 114].indexOf(mw.config.get('wgNamespaceNumber')) !== -1) // main, Talk, Citations
						return '"' + mw.config.get('wgTitle') + '"';
					if (mw.config.get('wgNamespaceNumber') === 100)
						if (m = /[^/]+\/(.*)/.exec(mw.config.get('wgTitle')))
							return m[1];
					if (mw.config.get('wgCanonicalSpecialPageName') === 'Search')
						return '"' + document.getElementById('searchText').value + '"';
					return '';
				})()
			}),
			el('input', null, {
				'type': 'submit',
				'value': "search"
			}), el('br'),
			uiBackends = el('div', [
				el('strong', "Backends"), ": "
			])
		])
	], {
		'action': 'javascript:void(0);'
	}, {
		'submit': function () {
			var hasAny = false;
			for (var key in backends) {
				hasAny = (function (backend) {
					if (!backend.enabled) {
						backend.uiTabLabel.style.display = 'none';
						return hasAny;
					} else {
						backend.uiTabLabel.style.display = '';
						if (!hasAny) {
							uiCurTab.style.display = 'none';
							uiCurTabLabel.classList.remove('active');
							uiCurTab = backend.uiTab;
							uiCurTabLabel = backend.uiTabLabel;
							uiCurTab.style.display = '';
							uiCurTabLabel.classList.add('active');
						}
					}
					
					backend.results = [];

					if (backend.enabled) {
						backend.search(uiQuery.value, null, function (result) {
							backend.results.push(result);

							var node = el('li');
							var checkbox = el('small', [
								"[",
								el('a', "pick", {
									'href': 'javascript:void(0)',
									'class': 'take-link'
								}, {
									click: function () {
										if (result.taken) {
											result.taken = false;
											node.classList.remove('taken');
										} else {
											result.taken = true;
											node.classList.add('taken');
										}
									}
								}),
								" | ",
								el('a', "collapse", { 'href': 'javascript:void(0)' }, {
									click: function () {
										node.classList.toggle('struck');
									}
								}),
								"] "
							]);
							result.populateNode(node, checkbox);
							backend.uiResults.appendChild(node);
						});
						
						return true;
					}
				})(backends[key]);
			}

			if (hasAny) {
				uiTabs.style.display = '';
				uiTabLabels.style.display = '';
			} else {
				alert("No backends chosen");
				uiTabs.style.display = 'none';
				uiTabLabels.style.display = 'none';
			}
		}
	}),
	uiTabLabels = el('ul', null, {
		'class': 'tab-labels',
		'style': 'display: none;'
	}),
	uiTabs = el('div', null, {
		'class': 'tabs',
		'style': 'display: none;'
	})
], {
	'class': 'kephir-qq',
	'style': 'display: none; position: fixed;'
});
jQuery(uiMain).draggable({
	handle: uiTitle
}).resizable({
	alsoResize: uiTabs
});
document.body.appendChild(uiMain);

var positions = JSON.parse(localStorage.QQ_positions || "{}");
var size = JSON.parse(localStorage.QQ_size || "{}");
$(function () {
    var d = $(".kephir-qq").attr("id", function (i) {
        return "draggable_" + i
    })
    $.each(positions, function (id, pos) {
        $("#" + id).css(pos)
    })
    $.each(size, function (id, siz) {
        $("#" + id).css(siz)
    })

    d.draggable({
        stop: function (event, ui) {
            positions[this.id] = ui.position
            localStorage.QQ_positions = JSON.stringify(positions)
        }
    });
    
    d.resizable({
        stop: function (event, ui) {
            size[this.id] = ui.size
            localStorage.QQ_size = JSON.stringify(size)
        }
    });
});

var uiCitationsHash;

function regenerateCitations() {
	var result = [];

	for (var key in backends) {
		var results = backends[key].results;
		for (var j = 0; j < results.length; ++j) {
			if (results[j].taken)
				result.push(results[j].generateCitation());
		}
	}
	result = result.join('');

	if (uiCitationsHash.checked) {
		result = result.replace(/^\*/mg, '#*').replace(/#*$/, '');
	}
	
	uiCitations.value = result;
}

uiTabs.appendChild(uiCurTab = uiCitationsTab = el('div', [
	uiCitations = el('textarea', null, {
		'rows': 20,
		'cols': 80
	}), el('br'),
	el('label', [
		uiCitationsHash = el('input', null, { 'type': 'checkbox' }, {
			'change': function () {
				regenerateCitations();
			}
		}),
		"Put # before each line"
	])
], { 'style': 'display: none' }));

uiTabLabels.appendChild(uiCurTabLabel = el('li', [
	el('a', "Picked citations", {
		'href': 'javascript:void(0);'
	}, {
		'click': function () {
			regenerateCitations();
			
			uiCurTab.style.display = 'none';
			uiCurTabLabel.classList.remove('active');
			uiCurTab = uiCitationsTab;
			uiCurTabLabel = this;
			uiCurTab.style.display = '';
			uiCurTabLabel.classList.add('active');
		}	
	})
], { 'class': 'citations-tab' }));

for (var key in backends) {
	(function (backend) {
		uiBackends.appendChild(el('label', [
			el('input', null, {
				'type': 'checkbox',
				'checked': backend.enabled ? 'checked' : ''
			}, {
				'change': function () {
					backend.enabled = this.checked;
				}
			}),
			backend.name
		]));
		
		backend.uiTab = el('div', [], {
			'style': 'display: none;'
		});
		backend.uiTabLabel = el('li', [
			el('a', backend.name, { 'href': 'javascript:void(0);' }, {
				'click': function () {
					uiCurTab.style.display = 'none';
					uiCurTabLabel.classList.remove('active');
					uiCurTab = backend.uiTab;
					uiCurTabLabel = backend.uiTabLabel;
					uiCurTab.style.display = '';
					uiCurTabLabel.classList.add('active');
				}	
			})
		]);
		backend.populateTab(backend.uiTab);
		uiTabs.appendChild(backend.uiTab);
		uiTabLabels.appendChild(backend.uiTabLabel);
	})(backends[key]);
}

var link = mw.util.addPortletLink(mw.config.get('skin') === 'vector' ? 'p-views' : 'p-cactions',
	'javascript:void(0);', 'QQ', 'p-kephir-qq', "search for attestations", '9'
);
link.addEventListener('click', function () {
	uiMain.style.display = '';
	uiQuery.focus();
}, false);