MediaWiki:Gadget-libTagger.js

Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
/**
 * This file is documented using
 * [jsduck-syntax](https://github.com/senchalabs/jsduck).
 * The source code is available at 
 * [Wikimedia Commons](https://commons.wikimedia.org/wiki/MediaWiki:Gadget-libTagger.js)
 * ([raw](https://commons.wikimedia.org/wiki?title=MediaWiki:Gadget-libTagger.js&action=raw&ctype=text/javascript))
 *
 * @class tagger
 * @singleton
 * 
 *
 *
 * Quick start: Simple usage:
 *
        @example 
        // Load the gadget
        mw.loader.using('ext.gadget.libTagger', function () {
            
        });
 *
 *
 * @requires jQuery
 * @requires mediaWiki
 * @requires libAPI
 * @requires wikiDOM
 *
 * @author Rainer Rillke <https://commons.wikimedia.org/wiki/User:Rillke>
 **/
 
/*
 * @license
 *   The MIT License (MIT) [full text see below]
 *   GNU General Public License, version 3 (GPL-3.0)
 *   Creative Commons Attribution 3.0 (CC-BY-3.0)
 * Choose whichever license of these you like best.
 *
 * @jshint valid
 * <http://jshint.com/>
 */
/*jshint curly:false, smarttabs:true*/

/*!
	Copyright (c) 2013 Rainer Rillke <https://commons.wikimedia.org/wiki/User:Rillke>

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
**/

(function($, mw, undefined) {
	'use strict';
	var tagger, defaultTagInfo;
	
	function firstValue(o) { for (var i in o) { if (o.hasOwnProperty(i)) { return o[i]; } } }
	
	/**
	 * @cfg defaultTagInfo
	 *  A skeleton showing how the tagInfo object should look like.
	 * @cfg {string} defaultTagInfo.text
	 *  Tag or text to add
	 * @cfg {string} defaultTagInfo.summary
	 *  Edit summary
	 * @cfg {number|string} defaultTagInfo.page
	 *  Page name or page ID
	 * @cfg {string} [defaultMessge.watchlist='watch']
	 *  Whether to watch the edited page. One of the following values:
	 *  watch, unwatch, preferences, nochange
	 * @cfg {string|Object} [defaultTagInfo.destination='prepend']
	 *  Where to add the text. Possible strings: prepend, append
	 *  or an object of the following pattern (to target to specific elements) 
	 *      {
	 *          location: 'before|after',
	 *          type: 'template|category',
	 *          target: ['Peferred', 'Second choice', 'Third choice'],
	 *          fallback: 'prepend|append'
	 *      }
	 */
	/**
	 * @property {Object} defaultTagInfo
	 *  Confer to ``defaultTagInfo`` configuration.
	 * @private
	 */
	defaultTagInfo = {
		text: "",
		summary: "",
		page: '',
		watchlist: 'watch',
		destination: 'prepend'
	};
	
	tagger = mw.libs.tagger = {
		/**
		 * Inserts a tag or text into the page specified.
		 *
		 * @param {Object} tagInfo
		 *  An object specifying the tag, summary and where to
		 *  insert that tag. Confer to ``defaultTagInfo`` config.
		 *
		 * @return {$.Promise} 
		 *  A jQuery Promise that is resolved after the tag was
		 *  inserted into the page.
		 *
             @example 
         // Load the gadget
         mw.loader.using('ext.gadget.libTagger', function () {
             mw.libs.tagger.tag({
					text: "{{Sand}} to be inserted into the sandbox.",
					summary: "Inserting a test text",
					page: 'Commons:Sandbox',
					watchlist: 'nochange',
					destination: 'append'
				 }).done(function() {
					 mw.libs.tagger.tag({
						text: " and pebbles",
						summary: "A bit inconvenient but also more funny.",
						page: 'Commons:Sandbox',
						watchlist: 'nochange',
						destination: {location: 'after', type: 'template', target: ['Sand'], fallback: 'append' }
					 });
				 });
         });
		 *
		 */
		tag: function(tagInfo) {
			tagInfo = $.extend({}, defaultTagInfo, tagInfo);
			
			if (tagInfo.destination.target) {
				return mw.libs.commons.api.$changeText(tagInfo.page, function(text) {
					var wikiDOM = mw.libs.wikiDOM,
						dom = wikiDOM.parser.text2Obj(text),
						destination = tagInfo.destination,
						type = destination.type,
						nodesOfType = dom.nodesByType[type],
						// TODO: Outsource to wikiDOM?
						clean = 'template' === type ? 'normalizeTemplateTransclusion' : 'normalizeLink',
						ed, inserted;

					if (nodesOfType) {
						$.each(destination.target, function(i, target) {
							target = wikiDOM[clean]( target );
							$.each(nodesOfType, function(i, n) {
								if (!n.parts) return;
								if (target === wikiDOM[clean]( n.parts[0][0] )) {
									//  node['after']('tag to add');
									n[destination.location](tagInfo.text);
									inserted = true;
									return false;
								}
							});
							if (inserted) return false;
						});
					}
					if (inserted) {
						text = wikiDOM.parser.obj2Text(dom);
					} else {
						switch (destination.fallback) {
							case 'append':
								text += tagInfo.text;
								break;
							case 'prepend':
								text = tagInfo.text + text;
								break;
							default:
								throw new Error('Unable to insert text and no fallback provided.');
						}
					}
					ed = $.extend({}, tagInfo, {
						text: text
					});
					return ed;
				});
			} else {
				var ed = $.extend({}, tagInfo, {
					title: tagInfo.page,
					editType: tagInfo.destination + 'text'
				});
				delete ed.page;
				delete ed.destination;
				return mw.libs.commons.api.$editPage( ed );
			}
		}
	};
	
}(jQuery, mediaWiki));
//</nowiki>