User:Ch1902/infobox.js

From Wikidata
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
$.expr.pseudos.template = $.expr.createPseudo(function (arg) {
   return function (elem) {
      return $.trim((elem.textContent || elem.innerText || jQuery(elem).text() || '')).toLowerCase() === arg.toLowerCase();
   };
});

/**
 * Widget to provide a toolbox link pull template data from wiki
 * articles and display the parameter names and parsed values in a 
 * draggable table on wikidata 
 */
$.widget('wd.infoboxview', {

defaults: {
   page: '',
   lang: 'en',
   templates: [],
   params: [],
   showEmpty: false
},

api: '',
table: '',
templates: [],
loaded: false,

_create: function () 
{
   var self = this;
   
   if ($.type(this.options.templates) === 'string')
      this.options.templates = [this.options.templates];
   
   $.each(this.options.templates, function (i, v) {
      self.options.templates[i] = v.replace(/_/g, ' ');
   });

   this.api = '//' + this.options.lang + '.wikipedia.org/w/api.php';
   this.wiki = '//' + this.options.lang + '.wikipedia.org/wiki/';
   
   this.table = $('<table/>').css({
         position: 'absolute',
         right: 15,
         top: 80,
         width: 400,
         zIndex: 1000001
      }).addClass('wikitable infoboxview-table').appendTo(document.body).hide();
      
   if ($.fn.draggable)
      this.table.draggable({handle: 'th > span'});
      
   this.element.on('click', function (e) { 
      self._run(e); 
      
      return false;
   });
   
   this.table.on('click', 'tr:has(th)', function (e) { 
      self._toggle(e); 
   });
   
   this.table.on('click', 'td', function (e) {
      self._displayEntity(e);
   });
   
   mw.util.addCSS(
      'table.infoboxview-table {font-size:small;border:1px solid #CCC;border-collapse:collapse}' +
      'table.infoboxview-table p {margin:0;}' +
      'table.infoboxview-table ul {list-style-type:none !important;list-style-image:none !important;margin:0;padding:0}' +
      'table.infoboxview-table li {list-style-type:none !important;list-style-image:none !important;margin:0;padding:0}' +
      'table.infoboxview-table .mw-redirected {color:darkgreen !important;font-weight:bold}' +
      'table.infoboxview-table .mw-redirecterr {color:#C00 !important;font-weight:bold}' +
      'table.infoboxview-table th {cursor:pointer}' +
      'table.infoboxview-table th span {cursor:move}'
   );
},

_toggle: function (e) 
{
   $('div.tipsy').hide();
   
   if ($.nodeName(e.target, 'span'))
      return true;
   
   $(e.currentTarget).nextUntil('tr:has(th)').toggle();
},

_run: function (e)
{
   var self = this;
   
   $('div.tipsy').hide();
   
   if (!e.shiftKey && this.loaded === true) {
      this.table.toggle();
      
      return;
   }
   
   if (!e.ctrlKey)
      $('.infoboxview-table').hide();  // hide all others
      
   this._loadTemplateCalls().done(function () {
      if (self.templates.length < 1)  // empty results
         self._displayNotice();
      else
         self._displayData();
         
      self.loaded = true;
   });
},

_displayNotice: function () {
   var table = this.table;
   
   table.empty();
   table.append('<tr><td colspan="2">' + this.options.page + ' contains no uses of ' + this.options.templates.join(', ') + '</td></tr>');
   table.show();
   table.css({top: $(window).scrollTop()});
},

_displayData: function () {
   var self = this, params = this.options.params, empty = this.options.showEmpty, table = this.table, 
       lang = this.options.lang, data;
   
   table.empty();
   table.show();
      
   table.css({top: $(window).scrollTop()});
   
   $.each(this.templates, function (_, template) {
      var data = params.length > 1 ? self._subsetValues(template, params, empty) : template, wikitable = self._objectToTable(data);
      
      $.when(self._parseWikiText(wikitable)).done(function (data) {
         var html = data && data.parse && data.parse.text && data.parse.text['*'] || '<table><tr><td colspan="2">Error</td></tr></table>';
             html = html.replace(/href\=\"\//gi, 'href="//' + lang + '.wikipedia.org/');
             
         var content = $(html);
          
         table.append('<tr><th colspan="2"><span>' + template.__title__ + '</span></th></tr>');
         table.append(content.find('tr'));
         
         table.find('a.mw-redirect').each(function () {
            var link = this, page = decodeURIComponent(link.href.split(/wiki\//)[1]);

            link.className = '';
            
            $.when(self._resolveRedirect(page)).done(function (data) {
               if (data && data.query && data.query.redirects && data.query.redirects[0]) {
                  link.href = self.wiki + mw.util.wikiUrlencode(data.query.redirects[0].to);
                  link.className += ' mw-redirected';
               } else {
                  link.className += ' mw-redirecterr';
               }
            });
         });
      });
   });
},

_displayEntity: function (e) {
   var self = this, td = $(e.currentTarget), next = td.next('td:has(a:not(.image, .new))'), links, requests, href, page, data;

   if (next.length === 0)
      return true;
   
   data = td.data();
   
   if (!data.tooltip) {
      links = next.find('a:not(.image, .new)'), requests = [];
      
      $.overlaySpinner(td, 'ib', 'small');
      
      links.each(function () {
         var href = this.href, page = decodeURIComponent(href.split(/\/wiki\//)[1].replace(/_/ig, ' '));
         
         requests.push($.ajax({
            url: '//www.wikidata.org/w/api.php',
            type: 'GET',
            dataType: 'json',
            data: {action: 'wbgetentities', format: 'json', props: 'info|sitelinks', sites: self.options.lang + 'wiki', titles: page}
         }));
      });
      
      $.when.apply($, requests).done(function () {
         var results = [], html = [];

         if (requests.length === 1) {
            results.push(new Array(arguments[0], arguments[1], arguments[2]));
         } else {
            results = arguments;
         }
         
         $.each(results, function (_, res) {
            var data = res[0], tooltip;
            
            if (!data) {
               tooltip = "Couldn't get data";
            } else if (data.error) {
               tooltip = data.error;
            } else if (data.entities && data.entities['-1']) {
               tooltip = '<a href="//www.wikidata.org/wiki/Special:ItemByTitle/' + self.options.lang + 'wiki/' + data.entities['-1'].title + '">No sitelinks found</a>';
            } else {
               $.each(data.entities, function (q, entity) {
                  var url = mw.util.getUrl(entity.title), desc = entity.sitelinks[self.options.lang + 'wiki'].title;
                  
                  tooltip = entity.title + '/<a href="' + url + '">' + entity.title + '</a> &ndash; ' + desc;
                  
                  return false;
               });
            }
            
            html.push('<div>' + tooltip + '</div>');
         });
         
         html = html.join("\n");
         
         self._displayTooltip(td, html);
         td.data({tooltip: html});
         
         $.removeOverlaySpinner('ib');
      });
   } else {
      self._displayTooltip(td, data.entity);
   }
},

_loadTemplateCalls: function () {
   var self = this;
   
   return $.ajax({
      url: this.api,
      type: 'GET',
      dataType: 'jsonp',
      data: {action: 'parse', format: 'json', redirects: 1, prop: '', generatexml: 1, page: this.options.page},
      error: function () { },
      success: function (data) {
         if (data && data.parse && data.parse.parsetree) {
            var xml = $.parseXML(data.parse.parsetree['*']), doc = $(xml), objects = [], selectors = [], templates;
            
            $.each(self.options.templates, function (_, template) {
               selectors.push('root > template:has(> title:template("' + template + '"))');
            });
            
            templates = doc.find(selectors.join(','));
            
            templates.each(function () {
               objects.push(self._constructTemplate($(this)));
            });
            
            self.templates = objects;
         }
      }
   });
},

_constructTemplate: function (template) {
   var self = this, tpl = {};
   
   tpl.__title__ = $.trim(template.find('> title').text());

   template.find('> part').each(function () {
      var name = $(this).find('> name'), value = $(this).find('> value').contents(), key = name.is(':empty') ? name.attr('index') : $.trim(name.text());
      
      if (value.length === 1 && value[0].nodeName === '#text') {  // just text
         tpl[key] = value.text();
      } else {
         tpl[key] = [];
         value.each(function () {
            if ($.nodeName(this, 'template')) {
               tpl[key].push(self._constructTemplate($(this)));
            } else {
               tpl[key].push(this.nodeValue);
            }
         });
      }
   });

   return tpl;
},

_objectToWikitext: function (obj) {
   var self = this, str;

   if ($.type(obj) === 'array') 
   {
      str = "";
      
      $.each(obj, function (_, value) {
         if ($.type(value) === 'string') {
            str += value;
         } else {
            str += self._objectToWikitext(value);
         }
      });
   } 
   else if ($.type(obj) === 'object')
   {
      str = "{{" + obj.__title__ + "\n";
      
      $.each(obj, function (key, value) {
         if (key === '__title__')
            return;
            
         if ($.type(value) === 'string') {
            str += "|" + key + "=" + value + "\n";
         } else {
            str += "|" + key + "=" + self._objectToWikitext(value) + "\n";
         }
      });
      
      str += "}}";
   }
   else
   {
      str = obj;
   }
   
   return str;
},

_displayTooltip: function (el, content) {
   var data = el.data();
   
   if (data && data.tipsy) {
      el.tipsy(el.tipsy('tip').is(':visible') ? 'hide' : 'show');
   } else {
      $(el).tipsy({
         trigger: 'manual',
         title: function () { return content; },
         html: true,
         gravity: 'e'
      });
      
      $(el).tipsy('show');
   }
},

_subsetValues: function (template, keys, empty) {
   var reduced = {};
   
   $.each(keys, function (_, value) {
      if ($.type(template[value]) !== 'undefined' && ((template[value] == '' && empty) || template[value] != ''))
         reduced[value] = template[value];
   });
   
   return reduced;
},

_objectToTable: function (template) {
   var self = this, str = "{|\n";
   
   $.each(template, function (name, value) {
      if (name === '__title__')
         return;
         
      str += "|-\n|class=\"" + name + "\"|" + name + " || " + self._objectToWikitext(value) + "\n";
   });
   
   str += "|}";
   
   return str;
},

_parseWikiText: function (text) {   
   return $.ajax({
      url: this.api,
      type: 'GET',
      dataType: 'jsonp',
      data: {action: 'parse', format: 'json', prop: 'text', text: text, redirects: 1, disablepp: 1},
   });
},

_resolveRedirect: function (page) {
   return $.ajax({
      url: this.api,
      type: 'GET',
      dataType: 'jsonp',
      data: {action: 'query', format: 'json', titles: page, redirects: 1},
   });
}

});