commit dec3863855d41c3f567a5228733596c8911c18f2 Author: Holger Brunn Date: Fri Jan 11 10:37:51 2013 +0100 [ADD] a wikimedia syntax parser, courtesy of http://en.wikipedia.org/wiki/User:Pilaf/InstaView This is already pretty usable, OpenERP specifics will have to be added as needed. diff --git a/wiki_wikimedia/__init__.py b/wiki_wikimedia/__init__.py new file mode 100644 index 00000000..25cf62ce --- /dev/null +++ b/wiki_wikimedia/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2012 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/wiki_wikimedia/__openerp__.py b/wiki_wikimedia/__openerp__.py new file mode 100644 index 00000000..278594b1 --- /dev/null +++ b/wiki_wikimedia/__openerp__.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2012 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +{ + 'name': 'Wiki - wikimedia syntax', + 'version': '1.0', + 'category': 'Knowledge Management', + 'complexity': "normal", + 'description': """ + Replace the standard parser by one that understands (more) wikimedia syntax + """, + 'author': 'Therp BV', + 'website': 'http://therp.nl', + 'depends': ['wiki'], + 'init_xml': [], + 'installable': True, + 'auto_install': False, + 'js': ['static/src/lib/instaview.js', 'static/src/js/wiki_wikimedia.js'], +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/wiki_wikimedia/static/src/js/wiki_wikimedia.js b/wiki_wikimedia/static/src/js/wiki_wikimedia.js new file mode 100644 index 00000000..c218c80e --- /dev/null +++ b/wiki_wikimedia/static/src/js/wiki_wikimedia.js @@ -0,0 +1,9 @@ +openerp.wiki_wikimedia = function (openerp) { + openerp.wiki.FieldWikiReadonly = openerp.web.page.FieldCharReadonly.extend({ + set_value: function (value) { + var show_value = InstaView.convert(value || ''); + this.$element.find('div').html(show_value); + return show_value; + } + }); +}; diff --git a/wiki_wikimedia/static/src/lib/instaview.js b/wiki_wikimedia/static/src/lib/instaview.js new file mode 100644 index 00000000..32b525b4 --- /dev/null +++ b/wiki_wikimedia/static/src/lib/instaview.js @@ -0,0 +1,989 @@ +/*

+This is a copy of InstaView for use in other applications like [[User:Cacycle/wikEd|wikEd]].
+ 
+Changes made:
+	Fixed code duplication, fixed "doubled article name" bug in links. Cacycle 00:56, 9 September 2007 (UTC)
+	The "Script to embed InstaView in MediaWiki's edit page" has been commented out
+	added: // get values from MediaWiki global variables (Cacycle)
+ 
+Installation:
+	if (typeof(InstaView) != 'object')) {
+		var script = document.createElement('script');
+		script.type = 'text/javascript';
+		script.src  = 'http://en.wikipedia.org/w/index.php?title=User:Pilaf/dev/instaview.js&action=raw&ctype=text/javascript&dontcountme=s';
+		document.getElementsByTagName('head')[0].appendChild(script);
+	}
+ 
+*/
+ 
+// Last update: Cacycle 22:26, 22 November 2008 (UTC)
+ 
+/*
+// Script to embed InstaView in MediaWiki's edit page
+addOnloadHook(function(){
+  if (document.getElementById('editpage-copywarn')) {
+    var oldPreview = document.getElementById('wpPreview');
+    var newPreview = document.createElement('input');
+    newPreview.setAttribute('type', 'button');
+    newPreview.setAttribute('style', 'font-style: italic');
+    newPreview.setAttribute('value', 'InstaView');
+    newPreview.setAttribute('id', 'InstaView');
+    newPreview.setAttribute('name', 'InstaView');
+    newPreview.setAttribute('onclick', "InstaView.dump('wpTextbox1', 'InstaViewDump')");
+    oldPreview.parentNode.insertBefore(newPreview, oldPreview);
+    oldPreview.parentNode.innerHTML += '
'; + oldPreview.value = 'Classic Preview'; + } +}); +*/ + +/* + * InstaView - a Mediawiki to HTML converter in JavaScript + * Version 0.6.1 + * Copyright (C) Pedro Fayolle 2005-2006 + * http://en.wikipedia.org/wiki/User:Pilaf + * Distributed under the BSD license + * + * Changelog: + * + * 0.6.1 + * - Fixed problem caused by \r characters + * - Improved inline formatting parser + * + * 0.6 + * - Changed name to InstaView + * - Some major code reorganizations and factored out some common functions + * - Handled conversion of relative links (i.e. [[/foo]]) + * - Fixed misrendering of adjacent definition list items + * - Fixed bug in table headings handling + * - Changed date format in signatures to reflect Mediawiki's + * - Fixed handling of [[:Image:...]] + * - Updated MD5 function (hopefully it will work with UTF-8) + * - Fixed bug in handling of links inside images + * + * To do: + * - Better support for + * - Full support for + * - Parser-based (as opposed to RegExp-based) inline wikicode handling (make it one-pass and bullet-proof) + * - Support for templates (through AJAX) + * - Support for coloured links (AJAX) + */ + + +var InstaView = {} + +// options +InstaView.conf = +{ + user: {}, + + wiki: { + lang: 'en', + interwiki: 'ab|aa|af|ak|sq|als|am|ang|ar|an|arc|hy|roa-rup|as|ast|av|ay|az|bm|ba|eu|be|bn|bh|bi|bs|br|bg|my|ca|ch|ce|chr|chy|ny|zh|zh-tw|zh-cn|cho|cv|kw|co|cr|hr|cs|da|dv|nl|dz|en|eo|et|ee|fo|fj|fi|fr|fy|ff|gl|ka|de|got|el|kl|gn|gu|ht|ha|haw|he|hz|hi|ho|hu|is|io|ig|id|ia|ie|iu|ik|ga|it|ja|jv|kn|kr|csb|ks|kk|km|ki|rw|rn|tlh|kv|kg|ko|kj|ku|ky|lo|la|lv|li|ln|lt|jbo|nds|lg|lb|mk|mg|ms|ml|mt|gv|mi|minnan|mr|mh|zh-min-nan|mo|mn|mus|nah|na|nv|ne|se|no|nn|oc|or|om|pi|fa|pl|pt|pa|ps|qu|ro|rm|ru|sm|sg|sa|sc|gd|sr|sh|st|tn|sn|scn|simple|sd|si|sk|sl|so|st|es|su|sw|ss|sv|tl|ty|tg|ta|tt|te|th|bo|ti|tpi|to|tokipona|ts|tum|tr|tk|tw|uk|ur|ug|uz|ve|vi|vo|wa|cy|wo|xh|ii|yi|yo|za|zu', + default_thumb_width: 180 + }, + + paths: { + articles: '/wiki/', + math: '/math/', + images: '', + images_fallback: 'http://upload.wikimedia.org/wikipedia/commons/', + magnify_icon: 'skins/common/images/magnify-clip.png' + }, + + locale: { + user: 'User', + image: 'Image', + category: 'Category', + months: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] + } +} + + +// get values from MediaWiki global variables (Cacycle) +if (typeof(wgArticlePath) != 'undefined') { InstaView.conf.paths.articles = wgArticlePath.replace(/\$1/, ''); } +if (typeof(wgContentLanguage) != 'undefined') { InstaView.conf.wiki.lang = wgContentLanguage; } + +// options with default values or backreferences +with (InstaView.conf) { + user.name = user.name || 'Wikipedian' + user.signature = '[['+locale.user+':'+user.name+'|'+user.name+']]' + paths.images = 'http://upload.wikimedia.org/wikipedia/' + wiki.lang + '/' +} + +// define constants +InstaView.BLOCK_IMAGE = new RegExp('^\\[\\['+InstaView.conf.locale.image+':.*?\\|.*?(?:frame|thumbnail|thumb|none|right|left|center)', 'i'); + +InstaView.dump = function(from, to) +{ + if (typeof from == 'string') from = document.getElementById(from) + if (typeof to == 'string') to = document.getElementById(to) + to.innerHTML = this.convert(from.value) +} + +InstaView.convert = function(wiki) +{ + var ll = (typeof wiki == 'string')? wiki.replace(/\r/g,'').split(/\n/): wiki, // lines of wikicode + o='', // output + p=0, // para flag + $r // result of passing a regexp to $() + + // some shorthands + function remain() { return ll.length } + function sh() { return ll.shift() } // shift + function ps(s) { o+=s } // push + + function f() // similar to C's printf, uses ? as placeholders, ?? to escape question marks + { + var i=1,a=arguments,f=a[0],o='',c,p + for (;i/g,">") } + + function max(a,b) { return (a>b)?a:b } + function min(a,b) { return (a= ipos; i--) { + + var pi = prev.charAt(i) + + if (pi=='*') ps('') + else if (pi=='#') ps('') + // close a dl only if the new item is not a dl item (:, ; or empty) + else switch (l_match[1].charAt(i)) { case'':case'*':case'#': ps('') } + } + + // open new lists + for (var i=ipos; i') + else if (li=='#') ps('
    ') + // open a new dl only if the prev item is not a dl item (:, ; or empty) + else switch(prev.charAt(i)) { case'':case'*':case'#': ps('
    ') } + } + + switch (l_match[1].charAt(l_match[1].length-1)) { + + case '*': case '#': + ps('
  1. ' + parse_inline_nowiki(l_match[2])); break + + case ';': + ps('
    ') + + var dt_match + + // handle ;dt :dd format + if (dt_match = l_match[2].match(/(.*?) (:.*?)$/)) { + + ps(parse_inline_nowiki(dt_match[1])) + ll.unshift(dt_match[2]) + + } else ps(parse_inline_nowiki(l_match[2])) + + break + + case ':': + ps('
    ' + parse_inline_nowiki(l_match[2])) + } + + prev=l_match[1] + } + + // close remaining lists + for (var i=prev.length-1; i>=0; i--) + ps(f('', (prev.charAt(i)=='*')? 'ul': ((prev.charAt(i)=='#')? 'ol': 'dl'))) + } + + function parse_table() + { + endl(f('', $(/^\{\|( .*)$/)? $r[1]: '')) + + for (;remain();) if ($('|')) switch (_(1)) { + case '}': endl(''); return + case '-': endl(f('', $(/\|-*(.*)/)[1])); break + default: parse_table_data() + } + else if ($('!')) parse_table_data() + else sh() + } + + function parse_table_data() + { + var td_line, match_i + + // 1: "|+", '|' or '+' + // 2: ?? + // 3: attributes ?? + // TODO: finish commenting this regexp + var td_match = sh().match(/^(\|\+|\||!)((?:([^[|]*?)\|(?!\|))?(.*))$/) + + if (td_match[1] == '|+') ps('') + + if (td_match[1] != '|+') { + + // use || or !! as a cell separator depending on context + // NOTE: when split() is passed a regexp make sure to use non-capturing brackets + td_line = td_match[match_i].split((td_match[1] == '|')? '||': /(?:\|\||!!)/) + + ps(parse_inline_nowiki(td_line.shift())) + + while (td_line.length) ll.unshift(td_match[1] + td_line.pop()) + + } else ps(td_match[match_i]) + + var tc = 0, td = [] + + for (;remain(); td.push(sh())) + if ($('|')) { + if (!tc) break // we're at the outer-most level (no nested tables), skip to td parse + else if (_(1)=='}') tc-- + } + else if (!tc && $('!')) break + else if ($('{|')) tc++ + + if (td.length) ps(InstaView.convert(td)) + } + + function parse_pre() + { + ps('
    ')
    +		do endl(parse_inline_nowiki(ll[0].substring(1)) + "\n"); while (remain() && $(' '))
    +		ps('
    ') + } + + function parse_block_image() + { + ps(parse_image(sh())) + } + + function parse_image(str) + { + // get what's in between "[[Image:" and "]]" + var tag = str.substring(InstaView.conf.locale.image.length + 3, str.length - 2); + + var width; + var attr = [], filename, caption = ''; + var thumb=0, frame=0, center=0; + var align=''; + + if (tag.match(/\|/)) { + // manage nested links + var nesting = 0; + var last_attr; + for (var i = tag.length-1; i > 0; i--) { + if (tag.charAt(i) == '|' && !nesting) { + last_attr = tag.substr(i+1); + tag = tag.substring(0, i); + break; + } else switch (tag.substr(i-1, 2)) { + case ']]': + nesting++; + i--; + break; + case '[[': + nesting--; + i--; + } + } + + attr = tag.split(/\s*\|\s*/); + attr.push(last_attr); + filename = attr.shift(); + + var w_match; + + for (;attr.length; attr.shift()) + if (w_match = attr[0].match(/^(\d*)px$/)) width = w_match[1] + else switch(attr[0]) { + case 'thumb': + case 'thumbnail': + thumb=true; + case 'frame': + frame=true; + break; + case 'none': + case 'right': + case 'left': + center=false; + align=attr[0]; + break; + case 'center': + center=true; + align='none'; + break; + default: + if (attr.length == 1) caption = attr[0]; + } + + } else filename = tag; + + + var o=''; + + if (frame) { + + if (align=='') align = 'right'; + + o += f("
    ", align); + + if (thumb) { + if (!width) width = InstaView.conf.wiki.default_thumb_width; + + o += f("
    ?", 2+width*1, make_image(filename, caption, width)) + + f("
    ?
    ", + InstaView.conf.paths.articles + InstaView.conf.locale.image + ':' + filename, + InstaView.conf.paths.magnify_icon, + parse_inline_nowiki(caption) + ) + } else { + o += '
    ' + make_image(filename, caption) + f("
    ?
    ", parse_inline_nowiki(caption)) + } + + o += '
    '; + + } else if (align != '') { + o += f("
    ?
    ", align, make_image(filename, caption, width)); + } else { + return make_image(filename, caption, width); + } + + return center? f("
    ?
    ", o): o; + } + + function parse_inline_nowiki(str) + { + var start, lastend=0 + var substart=0, nestlev=0, open, close, subloop; + var html=''; + + while (-1 != (start = str.indexOf('', substart))) { + html += parse_inline_wiki(str.substring(lastend, start)); + start += 8; + substart = start; + subloop = true; + do { + open = str.indexOf('', substart); + close = str.indexOf('', substart); + if (close<=open || open==-1) { + if (close==-1) { + return html + html_entities(str.substr(start)); + } + substart = close+9; + if (nestlev) { + nestlev--; + } else { + lastend = substart; + html += html_entities(str.substring(start, lastend-9)); + subloop = false; + } + } else { + substart = open+8; + nestlev++; + } + } while (subloop) + } + + return html + parse_inline_wiki(str.substr(lastend)); + } + + function make_image(filename, caption, width) + { + // uppercase first letter in file name + filename = filename.charAt(0).toUpperCase() + filename.substr(1); + // replace spaces with underscores + filename = filename.replace(/ /g, '_'); + + caption = strip_inline_wiki(caption); + + var md5 = hex_md5(filename); + + var source = md5.charAt(0) + '/' + md5.substr(0,2) + '/' + filename; + + if (width) width = "width='" + width + "px'"; + + var img = f("", InstaView.conf.paths.images_fallback + source, InstaView.conf.paths.images + source, (caption!='')? "alt='" + caption + "'" : '', width); + + return f("?", (caption!='')? "title='" + caption + "'" : '', InstaView.conf.paths.articles + InstaView.conf.locale.image + ':' + filename, img); + } + + function parse_inline_images(str) + { + var start, substart=0, nestlev=0; + var loop, close, open, wiki, html; + + while (-1 != (start=str.indexOf('[[', substart))) { + if(str.substr(start+2).match(RegExp('^' + InstaView.conf.locale.image + ':','i'))) { + loop=true; + substart=start; + do { + substart+=2; + close=str.indexOf(']]',substart); + open=str.indexOf('[[',substart); + if (close<=open||open==-1) { + if (close==-1) return str; + substart=close; + if (nestlev) { + nestlev--; + } else { + wiki=str.substring(start,close+2); + html=parse_image(wiki); + str=str.replace(wiki,html); + substart=start+html.length; + loop=false; + } + } else { + substart=open; + nestlev++; + } + } while (loop) + + } else break; + } + + return str; + } + + // the output of this function doesn't respect the FILO structure of HTML + // but since most browsers can handle it I'll save myself the hassle + function parse_inline_formatting(str) + { + var em,st,i,li,o=''; + while ((i=str.indexOf("''",li))+1) { + o += str.substring(li,i); + li=i+2; + if (str.charAt(i+2)=="'") { + li++; + st=!st; + o+=st?'':''; + } else { + em=!em; + o+=em?'':''; + } + } + return o+str.substr(li); + } + + function parse_inline_wiki(str) + { + var aux_match; + + str = parse_inline_images(str); + str = parse_inline_formatting(str); + + // math + while (aux_match = str.match(/<(?:)math>(.*?)<\/math>/i)) { + var math_md5 = hex_md5(aux_match[1]); + str = str.replace(aux_match[0], f("", InstaView.conf.paths.math+math_md5)); + } + + // Build a Mediawiki-formatted date string + var date = new Date; + var minutes = date.getUTCMinutes(); + if (minutes < 10) minutes = '0' + minutes; + var date = f("?:?, ? ? ? (UTC)", date.getUTCHours(), minutes, date.getUTCDate(), InstaView.conf.locale.months[date.getUTCMonth()], date.getUTCFullYear()); + + // text formatting + return str. + // signatures + replace(/~{5}(?!~)/g, date). + replace(/~{4}(?!~)/g, InstaView.conf.user.name+' '+date). + replace(/~{3}(?!~)/g, InstaView.conf.user.name). + + // [[:Category:...]], [[:Image:...]], etc... + replace(RegExp('\\[\\[:((?:'+InstaView.conf.locale.category+'|'+InstaView.conf.locale.image+'|'+InstaView.conf.wiki.interwiki+'):.*?)\\]\\]','gi'), "$1"). + replace(RegExp('\\[\\[(?:'+InstaView.conf.locale.category+'|'+InstaView.conf.wiki.interwiki+'):.*?\\]\\]','gi'),''). + + // [[/Relative links]] + replace(/\[\[(\/[^|]*?)\]\]/g, f("$1", location)). + + // [[/Replaced|Relative links]] + replace(/\[\[(\/.*?)\|(.+?)\]\]/g, f("$2", location)). + + // [[Common links]] + replace(/\[\[([^|]*?)\]\](\w*)/g, f("$1$2", InstaView.conf.paths.articles)). + + // [[Replaced|Links]] + replace(/\[\[(.*?)\|([^\]]+?)\]\](\w*)/g, f("$2$3", InstaView.conf.paths.articles)). + + // [[Stripped:Namespace|Namespace]] + replace(/\[\[([^\]]*?:)?(.*?)( *\(.*?\))?\|\]\]/g, f("$2", InstaView.conf.paths.articles)). + + // External links + replace(/\[(https?|news|ftp|mailto|gopher|irc):(\/*)([^\]]*?) (.*?)\]/g, "$4"). + replace(/\[http:\/\/(.*?)\]/g, "[#]"). + replace(/\[(news|ftp|mailto|gopher|irc):(\/*)(.*?)\]/g, "$1:$2$3"). + replace(/(^| )(https?|news|ftp|mailto|gopher|irc):(\/*)([^ $]*)/g, "$1$2:$3$4"). + + replace('__NOTOC__',''). + replace('__NOEDITSECTION__',''); + } + + function strip_inline_wiki(str) + { + return str + .replace(/\[\[[^\]]*\|(.*?)\]\]/g,'$1') + .replace(/\[\[(.*?)\]\]/g,'$1') + .replace(/''(.*?)''/g,'$1'); + } + + // begin parsing + for (;remain();) if ($(/^(={1,6})(.*)\1(.*)$/)) { + p=0 + endl(f('??', $r[1].length, parse_inline_nowiki($r[2]), $r[1].length, $r[3])) + + } else if ($(/^[*#:;]/)) { + p=0 + parse_list() + + } else if ($(' ')) { + p=0 + parse_pre() + + } else if ($('{|')) { + p=0 + parse_table() + + } else if ($(/^----+$/)) { + p=0 + endl('
    ') + + } else if ($(InstaView.BLOCK_IMAGE)) { + p=0 + parse_block_image() + + } else { + + // handle paragraphs + if ($$('')) { + if (p = (remain()>1 && ll[1]==(''))) endl('


    ') + } else { + if(!p) { + ps('

    ') + p=1 + } + ps(parse_inline_nowiki(ll[0]) + ' ') + } + + sh(); + } + + return o +} + + +/* + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * Version 2.2-alpha Copyright (C) Paul Johnston 1999 - 2005 + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for more info. + */ + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); } +function b64_md5(s) { return rstr2b64(rstr_md5(str2rstr_utf8(s))); } +function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); } +function hex_hmac_md5(k, d) + { return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); } +function b64_hmac_md5(k, d) + { return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); } +function any_hmac_md5(k, d, e) + { return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); } + +/* + * Calculate the MD5 of a raw string + */ +function rstr_md5(s) +{ + return binl2rstr(binl_md5(rstr2binl(s), s.length * 8)); +} + +/* + * Calculate the HMAC-MD5, of a key and some data (raw strings) + */ +function rstr_hmac_md5(key, data) +{ + var bkey = rstr2binl(key); + if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8); + + var ipad = Array(16), opad = Array(16); + for(var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); + return binl2rstr(binl_md5(opad.concat(hash), 512 + 128)); +} + +/* + * Convert a raw string to a hex string + */ +function rstr2hex(input) +{ + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var output = ""; + var x; + for(var i = 0; i < input.length; i++) + { + x = input.charCodeAt(i); + output += hex_tab.charAt((x >>> 4) & 0x0F) + + hex_tab.charAt( x & 0x0F); + } + return output; +} + +/* + * Convert a raw string to a base-64 string + */ +function rstr2b64(input) +{ + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var output = ""; + var len = input.length; + for(var i = 0; i < len; i += 3) + { + var triplet = (input.charCodeAt(i) << 16) + | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0) + | (i + 2 < len ? input.charCodeAt(i+2) : 0); + for(var j = 0; j < 4; j++) + { + if(i * 8 + j * 6 > input.length * 8) output += b64pad; + else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F); + } + } + return output; +} + +/* + * Convert a raw string to an arbitrary string encoding + */ +function rstr2any(input, encoding) +{ + var divisor = encoding.length; + var remainders = Array(); + var i, q, x, quotient; + + /* Convert to an array of 16-bit big-endian values, forming the dividend */ + var dividend = Array(input.length / 2); + for(i = 0; i < dividend.length; i++) + { + dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); + } + + /* + * Repeatedly perform a long division. The binary array forms the dividend, + * the length of the encoding is the divisor. Once computed, the quotient + * forms the dividend for the next step. We stop when the dividend is zero. + * All remainders are stored for later use. + */ + while(dividend.length > 0) + { + quotient = Array(); + x = 0; + for(i = 0; i < dividend.length; i++) + { + x = (x << 16) + dividend[i]; + q = Math.floor(x / divisor); + x -= q * divisor; + if(quotient.length > 0 || q > 0) + quotient[quotient.length] = q; + } + remainders[remainders.length] = x; + dividend = quotient; + } + + /* Convert the remainders to the output string */ + var output = ""; + for(i = remainders.length - 1; i >= 0; i--) + output += encoding.charAt(remainders[i]); + + return output; +} + +/* + * Encode a string as utf-8. + * For efficiency, this assumes the input is valid utf-16. + */ +function str2rstr_utf8(input) +{ + var output = ""; + var i = -1; + var x, y; + + while(++i < input.length) + { + /* Decode utf-16 surrogate pairs */ + x = input.charCodeAt(i); + y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0; + if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) + { + x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); + i++; + } + + /* Encode output as utf-8 */ + if(x <= 0x7F) + output += String.fromCharCode(x); + else if(x <= 0x7FF) + output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F), + 0x80 | ( x & 0x3F)); + else if(x <= 0xFFFF) + output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), + 0x80 | ((x >>> 6 ) & 0x3F), + 0x80 | ( x & 0x3F)); + else if(x <= 0x1FFFFF) + output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), + 0x80 | ((x >>> 12) & 0x3F), + 0x80 | ((x >>> 6 ) & 0x3F), + 0x80 | ( x & 0x3F)); + } + return output; +} + +/* + * Encode a string as utf-16 + */ +function str2rstr_utf16le(input) +{ + var output = ""; + for(var i = 0; i < input.length; i++) + output += String.fromCharCode( input.charCodeAt(i) & 0xFF, + (input.charCodeAt(i) >>> 8) & 0xFF); + return output; +} + +function str2rstr_utf16be(input) +{ + var output = ""; + for(var i = 0; i < input.length; i++) + output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, + input.charCodeAt(i) & 0xFF); + return output; +} + +/* + * Convert a raw string to an array of little-endian words + * Characters >255 have their high-byte silently ignored. + */ +function rstr2binl(input) +{ + var output = Array(input.length >> 2); + for(var i = 0; i < output.length; i++) + output[i] = 0; + for(var i = 0; i < input.length * 8; i += 8) + output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32); + return output; +} + +/* + * Convert an array of little-endian words to a string + */ +function binl2rstr(input) +{ + var output = ""; + for(var i = 0; i < input.length * 32; i += 8) + output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF); + return output; +} + +/* + * Calculate the MD5 of an array of little-endian words, and a bit length. + */ +function binl_md5(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << ((len) % 32); + x[(((len + 64) >>> 9) << 4) + 14] = len; + + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + + for(var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + + a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); + d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); + c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); + b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); + a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); + d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); + c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); + b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); + a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); + d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); + c = md5_ff(c, d, a, b, x[i+10], 17, -42063); + b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162); + a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); + d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); + c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290); + b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329); + + a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); + d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); + c = md5_gg(c, d, a, b, x[i+11], 14, 643717713); + b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); + a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); + d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083); + c = md5_gg(c, d, a, b, x[i+15], 14, -660478335); + b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); + a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); + d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); + c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); + b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); + a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); + d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); + c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); + b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734); + + a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); + d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); + c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562); + b = md5_hh(b, c, d, a, x[i+14], 23, -35309556); + a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); + d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); + c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); + b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640); + a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174); + d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); + c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); + b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); + a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); + d = md5_hh(d, a, b, c, x[i+12], 11, -421815835); + c = md5_hh(c, d, a, b, x[i+15], 16, 530742520); + b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); + + a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); + d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); + c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905); + b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); + a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); + d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); + c = md5_ii(c, d, a, b, x[i+10], 15, -1051523); + b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); + a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); + d = md5_ii(d, a, b, c, x[i+15], 10, -30611744); + c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); + b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649); + a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); + d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379); + c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); + b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + } + return Array(a, b, c, d); +} + +/* + * These functions implement the four basic operations the algorithm uses. + */ +function md5_cmn(q, a, b, x, s, t) +{ + return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); +} +function md5_ff(a, b, c, d, x, s, t) +{ + return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); +} +function md5_gg(a, b, c, d, x, s, t) +{ + return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); +} +function md5_hh(a, b, c, d, x, s, t) +{ + return md5_cmn(b ^ c ^ d, a, b, x, s, t); +} +function md5_ii(a, b, c, d, x, s, t) +{ + return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function bit_rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} \ No newline at end of file