FlightGear wiki:Instant-Refs: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
m (→‎Known Limitations: broken regexes: http://sourceforge.net/p/flightgear/mailman/message/31936560/)
(added an *experimental* jquery dialog output + escapeHtml for code snippets quoting (not yet used))
Line 49: Line 49:


== The Script ==
== The Script ==
<syntaxhighlight lang="javascript">// ==UserScript==
// ==UserScript==
// @name      instant-cquotes
// @name      instant-cquotes
// @description automatically create proper wikimedia cquotes for sourceforge ml and forum text selections
// @description automatically create proper wikimedia cquotes for sourceforge ml and forum text selections
Line 55: Line 55:
// @version    0.10
// @version    0.10
// @icon      http://wiki.flightgear.org/skins/common/images/icons-fg-135.png
// @icon      http://wiki.flightgear.org/skins/common/images/icons-fg-135.png
// @require    http://code.jquery.com/jquery-1.11.1.min.js
// @require    https://code.jquery.com/ui/1.11.0/jquery-ui.min.js
// @resource  jqUI_CSS  https://code.jquery.com/ui/1.11.0/themes/smoothness/jquery-ui.css
// @grant      GM_addStyle
// @grant      GM_getResourceText
// @match      http://sourceforge.net/p/flightgear/mailman/*  
// @match      http://sourceforge.net/p/flightgear/mailman/*  
// @match      http://forum.flightgear.org/*
// @match      http://forum.flightgear.org/*
// @copyright  2013+, FlightGear.org (bigstones, Philosopher & Hooray)
// @copyright  2013+, FlightGear.org (bigstones, Philosopher & Hooray)
// ==/UserScript==
// ==/UserScript==
 
var jqUI_CssSrc = GM_getResourceText ("jqUI_CSS");
GM_addStyle (jqUI_CssSrc);
 
var debug = 0;  
var debug = 0;  
 
/* content:
/* content:
  *    - 'selection' supports only getSelectedText, will support getSelectedHtml
  *    - 'selection' supports only getSelectedText, will support getSelectedHtml
Line 120: Line 128:
                         prepend("http://forum.flightgear.org")]
                         prepend("http://forum.flightgear.org")]
         }
         }
     }  
     }
};
};
 
var OUTPUT = {
var OUTPUT = OutputMethods().msgbox;
     // shows a message box  
 
    msgbox: function(msg) {
function OutputMethods() {
        if (window.prompt ("Copy to clipboard: Ctrl+C, Enter", msg) !== null)
     var methods = {
            window.getSelection().removeAllRanges(); // deselect all text
        // shows a message box  
     }
        msgbox: function(msg) {
            if (window.prompt ("Copy to clipboard: Ctrl+C, Enter", msg) !== null)
                window.getSelection().removeAllRanges(); // deselect all text
        },
       
        // XXX: experimental
        // show msg in a jQuery UI dialog
        jqueryDiag: function(msg) {
            var diagDiv = $('<div id="MyDialog"><textarea id="quotedtext" rows="10" cols="80" readonly>' + msg + '</textarea></div>');
           
            var diagParam = { title: "Copy your quote with CTRL-C",
                              modal: true,
                              buttons: [{ text: "Select all", click: function () { $(this).select(); } },
                                        { text: "Cancel", click: function () { $('quotedtext').dialog("close"); } }]
            };
                                       
            diagDiv.dialog(diagParam);
        }
       
     };
   
    return methods;
};
};
 
//////////////////////
//////////////////////
// TRANSFORM OPERATORS
// TRANSFORM OPERATORS
 
// prepend 'prefix'
// prepend 'prefix'
function prepend(prefix) {
function prepend(prefix) {
Line 140: Line 169:
     };
     };
}
}
 
// extract the first capture group in the regex (i.e. '(xxx)')  
// extract the first capture group in the regex (i.e. '(xxx)')  
function extract(regex) {
function extract(regex) {
Line 147: Line 176:
     };
     };
}
}
 
// replaces newlines with "unescaped" <br/>'s
// replaces newlines with "unescaped" <br/>'s
function newline2br() {
function newline2br() {
Line 154: Line 183:
     };
     };
}
}
 
// remove html comments (e.g. '<!-- this is a comment -->')
// remove html comments (e.g. '<!-- this is a comment -->')
function removeComments() {
function removeComments() {
Line 161: Line 190:
     };
     };
}
}
 
// converts html <a>...</a>'s to wiki links, internal if possible
// converts html <a>...</a>'s to wiki links, internal if possible
function a2wikilink() {
function a2wikilink() {
Line 172: Line 201:
         // links to the wiki with custom text (we preserve it)
         // links to the wiki with custom text (we preserve it)
         html = html.replace(/<a[^(?:href)]*href="https?:\/\/wiki.flightgear.org\/(.*?)".*?>(.*?)<\/a>/g, "[[$1|$2]]");
         html = html.replace(/<a[^(?:href)]*href="https?:\/\/wiki.flightgear.org\/(.*?)".*?>(.*?)<\/a>/g, "[[$1|$2]]");
       
         // then the others
         // then the others
         html = html.replace(/<a[^(?:href)]*href="(.*?)".*?>(.*?)<\/a>/g, "[$1 $2]");
         html = html.replace(/<a[^(?:href)]*href="(.*?)".*?>(.*?)<\/a>/g, "[$1 $2]");
Line 178: Line 207:
     };
     };
}
}
 
// converts embedded html images to external links,
// converts embedded html images to external links,
// but shows them as little images if they're from the wiki
// but shows them as little images if they're from the wiki
Line 196: Line 225:
     }
     }
}
}
 
// converts phpBB way of doing font style to the wiki way
// converts phpBB way of doing font style to the wiki way
function phpBB_fontstyle2wikistyle() {
function phpBB_fontstyle2wikistyle() {
Line 207: Line 236:
     };
     };
}
}
 
// converts (html) phpBB code blocks to wiki <syntaxhighlight>
// converts (html) phpBB code blocks to wiki <syntaxhighlight>
// FIXME: not actually using the above tag, because the copied html has
// FIXME: not actually using the above tag, because the copied html has
Line 217: Line 246:
     };
     };
}
}
 
// converts (html) phpBB quotes to simple wiki cquotes (author not cited, it'd get messy anyway)
// converts (html) phpBB quotes to simple wiki cquotes (author not cited, it'd get messy anyway)
function phpBB_quote2cquote() {
function phpBB_quote2cquote() {
Line 232: Line 261:
     }
     }
}
}
 
// escapes pipe characters that can be problematic inside a template.
// escapes pipe characters that can be problematic inside a template.
// Must be used BEFORE html tags were converted, or they get messed up.
// Must be used BEFORE html tags were converted, or they get messed up.
Line 242: Line 271:
     }
     }
}
}
 
// escapes the equal sign that can be problematic inside a template.
// escapes the equal sign that can be problematic inside a template.
// Must be used AFTER html tags were converted, or they get messed up.
// Must be used AFTER html tags were converted, or they get messed up.
Line 250: Line 279:
     }
     }
}
}
 
 
///////////////////
///////////////////
// SCRIPT MAIN BODY
// SCRIPT MAIN BODY
 
window.addEventListener('load', register);
window.addEventListener('load', register);
 
function register() {
function register() {
     // check if we have a matching domain in the CONFIG hash
     // check if we have a matching domain in the CONFIG hash
Line 263: Line 292:
     }
     }
}
}
 
// main function
// main function
function instantCquote() {
function instantCquote() {
     if(getSelectedText() === "") return;
     if(getSelectedText() === "") return;
   
     var profile = CONFIG[window.location.hostname];
     var profile = CONFIG[window.location.hostname];
     var parent = getSelectionParent();
     var parent = getSelectionParent();
   
     var info = parent ? extractInfo(profile, parent) : extractInfoFallback();
     var info = parent ? extractInfo(profile, parent) : extractInfoFallback();
   
     if (info) {
     if (info) {
         var cquote = createCquote(info);
         var cquote = createCquote(info);
         OUTPUT.msgbox(cquote);
         OUTPUT(cquote);
     } else {
     } else {
         alert("info == null");
         alert("info == null");
     }
     }
}
}
 
function extractInfoFallback() { // XXX untested
function extractInfoFallback() { // XXX untested
     var info = {};
     var info = {};
Line 290: Line 319:
     return info;
     return info;
}
}
 
function extractInfo(profile, parent) {
function extractInfo(profile, parent) {
     var info = {};
     var info = {};
   
     for (var field in profile) {
     for (var field in profile) {
         if (!profile.hasOwnProperty(field)) continue;  
         if (!profile.hasOwnProperty(field)) continue;  
       
            // this part gets the data, both for field and content (i.e. text, html)
        // this part gets the data, both for field and content (i.e. text, html)
            var fieldInfo = extractFieldInfo(profile, parent, field);
        var fieldInfo = extractFieldInfo(profile, parent, field);
       
            if (debug) alert("pre trans:\n" + field + " = " + fieldInfo);
        if (debug) alert("pre trans:\n" + field + " = " + fieldInfo);
       
            var transform = profile[field].transform;
        var transform = profile[field].transform;
            if(transform) fieldInfo = applyTransformations(fieldInfo, transform);
        if(transform) fieldInfo = applyTransformations(fieldInfo, transform);
       
            if (debug) alert("post trans:\n" + field + " = " + fieldInfo);
        if (debug) alert("post trans:\n" + field + " = " + fieldInfo);
       
            info[field] = fieldInfo;
        info[field] = fieldInfo;
       
     }
     }
   
     return info;
     return info;
}
}
 
function extractFieldInfo(profile, parent, field) {
function extractFieldInfo(profile, parent, field) {
     if (field != "content") {
     if (field != "content") {
Line 324: Line 353:
     }
     }
}
}
 
function applyTransformations(fieldInfo, trans) {
function applyTransformations(fieldInfo, trans) {
     if(typeof trans === "function") {
     if(typeof trans === "function") {
Line 337: Line 366:
     }
     }
}
}
 
function createCquote(info) {
function createCquote(info) {
     //TODO: add template string to profile
     //TODO: add template string to profile
Line 350: Line 379:
     return template;
     return template;
}
}
 
function createForumQuote(info) {
function createForumQuote(info) {
     //TODO: add template string to profile
     //TODO: add template string to profile
Line 358: Line 387:
     return template;
     return template;
}
}
 
function nowiki(text) {
function nowiki(text) {
     return "<nowiki>" + text + "</nowiki>";
     return "<nowiki>" + text + "</nowiki>";
}
}
 
////////////////////
////////////////////
// UTILITY FUNCTIONS
// UTILITY FUNCTIONS
 
// IE8 compat. function
// IE8 compat. function
function getSelectionParent() {
function getSelectionParent() {
Line 372: Line 400:
         // anchorNode is the textnode, parentNode is the parent html element
         // anchorNode is the textnode, parentNode is the parent html element
         return document.getSelection().anchorNode.parentNode;
         return document.getSelection().anchorNode.parentNode;
       
     } else if (document.selection) { // for IE <= v8 - XXX: not tested!
     } else if (document.selection) { // for IE <= v8 - XXX: not tested!
         return document.selection.createRange().parentElement();
         return document.selection.createRange().parentElement();
     }
     }
}
}
 
// IE8 compat. function
// IE8 compat. function
function getSelectedText() {
function getSelectedText() {
     if(document.getSelection) { // for modern, standard compliant browsers
     if(document.getSelection) { // for modern, standard compliant browsers
         return document.getSelection().toString();
         return document.getSelection().toString();
       
     } else if (document.selection) { // for IE <= v8 - XXX: not tested!
     } else if (document.selection) { // for IE <= v8 - XXX: not tested!
         return document.selection.createRange().text;
         return document.selection.createRange().text;
     }
     }
}
}
 
// IE8 compat. function (copied from http://stackoverflow.com/a/6668159 )
// IE8 compat. function (copied from http://stackoverflow.com/a/6668159 )
function getSelectedHtml() {
function getSelectedHtml() {
Line 405: Line 433:
         }
         }
     }
     }
    return html;
        return html;
}
}
 
// copied from http://stackoverflow.com/a/2631931
// copied from http://stackoverflow.com/a/2631931
function getXPathTo(element) {
function getXPathTo(element) {
Line 414: Line 442:
     if (element === document.body)
     if (element === document.body)
         return element.tagName;
         return element.tagName;
   
     var ix= 0;
     var ix= 0;
     var siblings = element.parentNode.childNodes;
     var siblings = element.parentNode.childNodes;
Line 424: Line 452:
             ix++;
             ix++;
     }
     }
}
// copied from http://stackoverflow.com/a/12034334
var entityMap = {
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    '"': '&quot;',
    "'": '&#39;',
    "/": '&#x2F;',
    "\n": "<br/>"
};
function escapeHtml(string) {
    return String(string).replace(/[&<>"'\/]|[\n]/g, function (s) {
        return entityMap[s];
    });
}</syntaxhighlight>
}</syntaxhighlight>


[[Category:Wiki maintenance]]
[[Category:Wiki maintenance]]

Revision as of 14:18, 29 June 2014

Note  FlightGear development is not centrally coordinated in any way - at the very best, FlightGear development is "self-coordinated" - i.e. contributors discuss ideas and make proposals to contribute in a certain fashion and then team up to implement certain features and building blocks temporarily.

Obviously, ideas and feature requests made by end-users are also appreciated. But at the end of the day, people who do the actual work have obviously more say about future development than those who merely make suggestions. And contributors tend to prioritize work on items that are prioritized/suggested by other contributors, especially those offering to get involved and help in some way, and of those having some kind of track record in the form of past contributions.

But given the lack of development manpower, many good ideas tend to have a fairly long shelf life unfortunately. So that good ideas may be forgotten over time.

This is also why it is important for other developers/contributors to know who came up with a certain idea and who originally supported/supports it, possibly months (or even years) after a discussion took place - which is why good ideas should not just be preserved, but accompanied by corresponding quotes, linking back to the original discussion, so that potential contributors can make up their own minds to determine if/how they want to get involved in some effort or not. The script that you can find below is intended to help with this (as well as allowing people to easily reuse forum/devel list announcements in wiki articles, e.g. to update the changelog, newsletter or "release plan/lessons learnt" section) - it allows people to easily copy&paste important discussions over to the wiki, without having to rewrite any text, and without having to put together proper cquotes manually. In fact, it's a matter of just a few seconds. Use this article's discussion page to get in touch (e.g. if there are any bugs/problems or features requests).

Example Output

The output may look like this (click the link to see the original forum posting):

Cquote1.png The upcoming FlightGear version (3.2) will contain a canvas-based map dialog, including a modular "plugin" system for creating custom map layers and charts with roughly ~50 lines of code, most of it boilerplate.


This is entirely XML/Nasal based (scripted) - symbols can be pretty much anything, raster or vector images (png or svg), but even animated. Styling can be customized, too.

For more info, I suggest to check out:

MapStructure#Porting_the_map_dialog
MapStructureDialog.png


— Hooray (Sat Jun 14). Re: Get objects to show up on Map/Radar.
(powered by Instant-Cquotes)
Cquote2.png

Background & Installation

  • A simple userscript extension implemented in JavaScript
  • Supported by FireFox, Chrome, Chromium, probably Opera and others
  • in case of doubt, just install the GreaseMonkey/TamperMonkey extension
  • install the script (on firefox, save the script as instant_cquotes.user.js, then drag'n'drop it into firefox).

Usage

or to any forum message, such as: http://forum.flightgear.org/viewtopic.php?f=71&t=23299#p212558
  • copy/mark some relevant portion of text (the script converts links, images and even youtube videos - wiki images are properly converted, too and it will try to retain basic formatting)
  • and directly get a full cquote paragraph (including author, date, thread and URL) that you can add to the wiki here using CTRL+C & CTRL-V

Known Limitations

  • newline2br addNewline() should also insert CR/LF for separating paragraphs in the cquote (also, the trailing slash is not added) Done Done
  • quoting code doesn't yet work properly Not done Not done
  • our regexes may fail once the HTML DOM of the source changes (e.g. phpBB/theme update), so we should better show a warning when that's the case [1]Not done Not done
  • it may make sense to provide multiple regexes that are tried in order Not done Not done
  • The script uses a conventional JavaScript alert() box for providing the output, these dialogs are typically restricted to a max size of ~10kb-we may need to explore other/extended options Not done Not done
  • The script has already seen several iterations and created dozens of cquotes we're using in various places, but it might be possible to use the script to update previously created cquotes/FGCquotes automatically, instead of using the getSelection() helper, we could register a match for wiki.flightgear.org with the action=edit set, so that we can directly process all text of an edited page (using AJAX calls to open the URL in the background would make sense) Not done Not done

Feature Requests & Ideas

  • gitorious links converted to wiki markup could/should probably use the "Git link" template, e.g. see the quote at [2]

The Script

// ==UserScript== // @name instant-cquotes // @description automatically create proper wikimedia cquotes for sourceforge ml and forum text selections // @namespace http://wiki.flightgear.org/ // @version 0.10 // @icon http://wiki.flightgear.org/skins/common/images/icons-fg-135.png // @require http://code.jquery.com/jquery-1.11.1.min.js // @require https://code.jquery.com/ui/1.11.0/jquery-ui.min.js // @resource jqUI_CSS https://code.jquery.com/ui/1.11.0/themes/smoothness/jquery-ui.css // @grant GM_addStyle // @grant GM_getResourceText // @match http://sourceforge.net/p/flightgear/mailman/* // @match http://forum.flightgear.org/* // @copyright 2013+, FlightGear.org (bigstones, Philosopher & Hooray) // ==/UserScript==

var jqUI_CssSrc = GM_getResourceText ("jqUI_CSS"); GM_addStyle (jqUI_CssSrc);

var debug = 0;

/* content:

*    - 'selection' supports only getSelectedText, will support getSelectedHtml
*    - 'transform' takes a "tranform operator", or an ordered array of operators
* author, title, date, url:
*    - 'xpath' takes the path to the field, relative to the html parent of the selected text
*    - 'transform' takes a "transform operator", or an ordered array of operators
  • /

var CONFIG = {

   "sourceforge.net": {
       content: {
           selection: getSelectedText,
           transform: newline2br()
       }, 
       author: {
           xpath: "../../../tr/td/div/small/text()",
           transform: extract(/^From: (.*) <.*@.*>/)
       },
       title: {
           xpath: "../../../tr/td/div/div/b/a/text()"
       },
       date: {
           xpath: "../../../tr/td/div/small/text()",
           transform: extract(/ - (.*-.*-.*) /)
       },
       url: {
           xpath: "../../../tr/td/div/div/b/a/@href",
           transform: prepend("http://sourceforge.net")
       }
   },
   "forum.flightgear.org": {
       content: {
           selection: getSelectedHtml,
           transform: [removeComments(),
                       escapePipes(),
                       a2wikilink(),
                       phpBB_smilies2text(),
                       img2link(),
                       phpBB_fontstyle2wikistyle(),
                       phpBB_code2syntaxhighlight(), // FIXME: could be much better, see below
                       phpBB_quote2cquote(),
                       escapeEquals(),
                       addNewlines()]
       },
       author: {
           xpath: "../p/strong/a/text()"
       },
       title: {
           xpath: "../h3/a/text()"
       },
       date: {
           xpath: "../p/text()[2]",
           transform: extract(/ » (.*),/)
       },
       url: {
           xpath: "../p/a/@href",
           transform: [extract(/\.(.*)/),
                       prepend("http://forum.flightgear.org")]
       }
   }

};

var OUTPUT = OutputMethods().msgbox;

function OutputMethods() {

   var methods = {
       // shows a message box 
       msgbox: function(msg) {
           if (window.prompt ("Copy to clipboard: Ctrl+C, Enter", msg) !== null)
               window.getSelection().removeAllRanges(); // deselect all text
       },
       
       // XXX: experimental
       // show msg in a jQuery UI dialog
       jqueryDiag: function(msg) {

var diagDiv = $('

<textarea id="quotedtext" rows="10" cols="80" readonly>' + msg + '</textarea>

');

           var diagParam = { title: "Copy your quote with CTRL-C",
                             modal: true,
                             buttons: [{ text: "Select all", click: function () { $(this).select(); } },
                                       { text: "Cancel", click: function () { $('quotedtext').dialog("close"); } }]
           };
                                        
           diagDiv.dialog(diagParam);
       }
       
   };
   
   return methods;

};

////////////////////// // TRANSFORM OPERATORS

// prepend 'prefix' function prepend(prefix) {

   return function(text) {
       return prefix+text;
   };

}

// extract the first capture group in the regex (i.e. '(xxx)') function extract(regex) {

   return function(text) {
       return text.match(regex)[1];
   };

}

// replaces newlines with "unescaped"
's function newline2br() {

   return function(text) {
       return text.replace(/\n/g,"
\n"); };

}

// remove html comments (e.g. ) function removeComments() {

   return function(html) {
       return html.replace(//g,"");
   };

}

// converts html <a>...</a>'s to wiki links, internal if possible function a2wikilink() {

   return function(html) {
       // first tries internal links, firstmost links to images (File:xxx), because
       // they need special treatment, or they get displayed
       html = html.replace(/<a[^(?:href)]*href="https?:\/\/wiki.flightgear.org\/(File:.*?)".*?>(.*?)<\/a>/g, "$2");
       // automatic links to the wiki (we don't use the displayed text)
       html = html.replace(/<a[^(?:href)]*href="https?:\/\/wiki.flightgear.org\/(.*?)".*?>https?:\/\/wiki.flightgear.org.*?<\/a>/g, "$1");
       // links to the wiki with custom text (we preserve it)
       html = html.replace(/<a[^(?:href)]*href="https?:\/\/wiki.flightgear.org\/(.*?)".*?>(.*?)<\/a>/g, "$2");
       
       // then the others
       html = html.replace(/<a[^(?:href)]*href="(.*?)".*?>(.*?)<\/a>/g, "[$1 $2]");
       return html;
   };

}

// converts embedded html images to external links, // but shows them as little images if they're from the wiki function img2link() {

   return function(html) {
       html = html.replace(/<img.*?src="https?:\/\/wiki.flightgear.org\/images\/.*?\/(?:[0-9]+px-)?([^\/]*?)".*?>/g, "File:$1");
       return html.replace(/<img.*?src="(.*?)".*?>/g, "(see the [$1 linked image])");
   };

}

// puts newlines where it makes for more readable wiki "source" function addNewlines() {

   return function(html) {
       html = html.replace(/<br\/?>/g,"
\n"); html = html.replace(/(<\/?[uo]l>)/g,"$1\n");

return html.replace(/<\/li>/g,"\n"); } } // converts phpBB way of doing font style to the wiki way function phpBB_fontstyle2wikistyle() { return function(bbhtml) { bbhtml = bbhtml.replace(/

(.*?)<\/span>/g, "$1"); bbhtml = bbhtml.replace(/(.*?)<\/span>/g, "$1"); bbhtml = bbhtml.replace(/(.*?)<\/span>/g, "$1"); bbhtml = bbhtml.replace(/(.*?)<\/span>/g, "$1"); return bbhtml; }; } // converts (html) phpBB code blocks to wiki
// FIXME: not actually using the above tag, because the copied html has
//        unconverted html entities and br's are not interpreted.
//        Solution would be to extract the code, fix it and use the proper tag...
function phpBB_code2syntaxhighlight() {
    return function(bbhtml) {
        return bbhtml.replace(/<dl.*?><dt>.*?<\/dt><dd><code>(.*?)<\/code><\/dd><\/dl>/g, "<tt>\n$1\n</tt>");
    };
}

// converts (html) phpBB quotes to simple wiki cquotes (author not cited, it'd get messy anyway)
function phpBB_quote2cquote() {
    return function(bbhtml) {
        return bbhtml.replace(/<blockquote.*?><div>(?:<cite>.*?<\/cite>)?(.*?)<\/div><\/blockquote>/g, "{{cquote|$1}}");
    };
}

// converts smilies images from phpBB.
// Must be used BEFORE img2link(), because it makes a broken link of them
function phpBB_smilies2text() {
    return function(bbhtml) {
        return bbhtml.replace(/<img.*?src="\.\/images\/smilies\/icon_.*?\.gif".*?alt="(.*?)".*?>/g,"$1");
    }
}

// escapes pipe characters that can be problematic inside a template.
// Must be used BEFORE html tags were converted, or they get messed up.
function escapePipes() {
    return function(text) {
        text = text.replace(/\|\|/g,"{{!!}}");
        text = text.replace(/\|\-/g,"{{!-}}");
        return text.replace(/\|/g,"{{!}}");
    }
}

// escapes the equal sign that can be problematic inside a template.
// Must be used AFTER html tags were converted, or they get messed up.
function escapeEquals() {
    return function(text) {
        return text.replace(/=/g,"{{=}}");
    }
}


///////////////////
// SCRIPT MAIN BODY

window.addEventListener('load', register);

function register() {
    // check if we have a matching domain in the CONFIG hash
    if (CONFIG.hasOwnProperty(window.location.hostname)) {
        document.onmouseup = instantCquote; // register the callback for "mouse button up" event
    }
}

// main function
function instantCquote() {
    if(getSelectedText() === "") return;
    
    var profile = CONFIG[window.location.hostname];
    var parent = getSelectionParent();
    
    var info = parent ? extractInfo(profile, parent) : extractInfoFallback();
    
    if (info) {
        var cquote = createCquote(info);
        OUTPUT(cquote);
    } else {
        alert("info == null");
    }
}

function extractInfoFallback() { // XXX untested
    var info = {};
    info.content = getSelectedText();
    info.url = document.URL;
    info.title = "from " + window.location.hostname;
    info.author = "";
    info.date = "";
    return info;
}

function extractInfo(profile, parent) {
    var info = {};
    
    for (var field in profile) {
        if (!profile.hasOwnProperty(field)) continue; 
        
        // this part gets the data, both for field and content (i.e. text, html)
        var fieldInfo = extractFieldInfo(profile, parent, field);
        
        if (debug) alert("pre trans:\n" + field + " = " + fieldInfo);
        
        var transform = profile[field].transform;
        if(transform) fieldInfo = applyTransformations(fieldInfo, transform);
        
        if (debug) alert("post trans:\n" + field + " = " + fieldInfo);
        
        info[field] = fieldInfo;
        
    }
    
    return info;
}

function extractFieldInfo(profile, parent, field) {
    if (field != "content") {
        var xpath = profile[field].xpath;
        var parentXPath = getXPathTo(parent);
        var fullPath =  parentXPath + "/" + xpath;
        return document.evaluate(fullPath, document, null, XPathResult.STRING_TYPE, null).stringValue;
    } else {
        return profile[field].selection(); // here we extract the contents of the selection
    }
}

function applyTransformations(fieldInfo, trans) {
    if(typeof trans === "function") {
        return trans(fieldInfo);
    } else if (Array.isArray(trans)) {
        for (var i = 0; i < trans.length; i++) {
            if (debug) alert("pre trans in array:\n = " + fieldInfo);
            fieldInfo = trans[i](fieldInfo);
            if (debug) alert("post trans in array:\n = " + fieldInfo);
        }
        return fieldInfo;
    }
}

function createCquote(info) {
    //TODO: add template string to profile
    var template = "{{FGCquote" + "\n" +
        "  |" + info.content + "\n" +
        "  |{{cite web |url=" + info.url + "\n" +
        "     |title=" + nowiki(info.title) + "\n" +
        "     |author=" + nowiki(info.author) + "\n" +
        "     |date=" + nowiki(info.date) + "\n" +
        "   }}" + "\n" +
        "}}";
    return template;
}

function createForumQuote(info) {
    //TODO: add template string to profile
    // nowiki(info.date)
    var template = "[url='"+info.url+"']"+info.title+"("+nowiki(info.date)+")[/url][quote='"+nowiki(info.author)+"']" + nowiki(info.content) + "\n" +
        "[/quote]";
    return template;
}

function nowiki(text) {
    return "<nowiki>" + text + "</nowiki>";
}

////////////////////
// UTILITY FUNCTIONS

// IE8 compat. function
function getSelectionParent() {
    if(document.getSelection) { // for modern, standard compliant browsers
        // anchorNode is the textnode, parentNode is the parent html element
        return document.getSelection().anchorNode.parentNode;
        
    } else if (document.selection) { // for IE <= v8 - XXX: not tested!
        return document.selection.createRange().parentElement();
    }
}

// IE8 compat. function
function getSelectedText() {
    if(document.getSelection) { // for modern, standard compliant browsers
        return document.getSelection().toString();
        
    } else if (document.selection) { // for IE <= v8 - XXX: not tested!
        return document.selection.createRange().text;
    }
}

// IE8 compat. function (copied from http://stackoverflow.com/a/6668159 )
function getSelectedHtml() {
    var html = "";
    if (typeof window.getSelection != "undefined") {
        var sel = window.getSelection();
        if (sel.rangeCount) {
            var container = document.createElement("div");
            for (var i = 0, len = sel.rangeCount; i < len; ++i) {
                container.appendChild(sel.getRangeAt(i).cloneContents());
            }
            html = container.innerHTML;
        }
    } else if (typeof document.selection != "undefined") {
        if (document.selection.type == "Text") {
            html = document.selection.createRange().htmlText;
        }
    }
        return html;
}

// copied from http://stackoverflow.com/a/2631931
function getXPathTo(element) {
    if (element.id !== '')
        return 'id("' + element.id + '")';
    if (element === document.body)
        return element.tagName;
    
    var ix= 0;
    var siblings = element.parentNode.childNodes;
    for (var i= 0; i<siblings.length; i++) {
        var sibling = siblings[i];
        if (sibling === element)
            return getXPathTo(element.parentNode) + '/' + element.tagName + '[' + (ix+1) + ']';
        if (sibling.nodeType === 1 && sibling.tagName === element.tagName)
            ix++;
    }
}

// copied from http://stackoverflow.com/a/12034334
var entityMap = {
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    '"': '&quot;',
    "'": '&#39;',
    "/": '&#x2F;',
    "\n": "<br/>"
};

function escapeHtml(string) {
    return String(string).replace(/[&<>"'\/]|[\n]/g, function (s) {
        return entityMap[s];
    });
}