/*
 *  Copyright 2011 Twitter, Inc.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */


var HoganTemplate = (function() {

    function constructor(text) {
        this.text = text;
    }

    constructor.prototype = {

        // render: replaced by generated code.
        r: function(context, partials, indent) {
            return '';
        },

        // variable escaping
        v: hoganEscape,

        render: function render(context, partials, indent) {
            return this.r(context, partials, indent);
        },

        // tries to find a partial in the curent scope and render it
        rp: function(name, context, partials, indent) {
            var partial = partials[name];

            if (!partial) {
                return '';
            }

            return partial.r(context, partials, indent);
        },

        // render a section
        rs: function(context, partials, section) {
            var buf = '',
                tail = context[context.length - 1];

            if (!isArray(tail)) {
                return buf = section(context, partials);
            }

            for (var i = 0; i < tail.length; i++) {
                context.push(tail[i]);
                buf += section(context, partials);
                context.pop();
            }

            return buf;
        },

        // maybe start a section
        s: function(val, ctx, partials, inverted, start, end, tags) {
            var pass;

            if (isArray(val) && val.length === 0) {
                return false;
            }

            if (!inverted && typeof val == 'function') {
                val = this.ls(val, ctx, partials, start, end, tags);
            }

            pass = (val === '') || !! val;

            if (!inverted && pass && ctx) {
                ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]);
            }

            return pass;
        },

        // find values with dotted names
        d: function(key, ctx, partials, returnFound) {

            var names = key.split('.'),
                val = this.f(names[0], ctx, partials, returnFound),
                cx = null;

            if (key === '.' && isArray(ctx[ctx.length - 2])) {
                return ctx[ctx.length - 1];
            }

            for (var i = 1; i < names.length; i++) {
                if (val && typeof val == 'object' && names[i] in val) {
                    cx = val;
                    val = val[names[i]];
                } else {
                    val = '';
                }
            }

            if (returnFound && !val) {
                return false;
            }

            if (!returnFound && typeof val == 'function') {
                ctx.push(cx);
                val = this.lv(val, ctx, partials);
                ctx.pop();
            }

            return val;
        },

        // find values with normal names
        f: function(key, ctx, partials, returnFound) {
            var val = false,
                v = null,
                found = false;

            for (var i = ctx.length - 1; i >= 0; i--) {
                v = ctx[i];
                if (v && typeof v == 'object' && key in v) {
                    val = v[key];
                    found = true;
                    break;
                }
            }

            if (!found) {
                return (returnFound) ? false : "";
            }

            if (!returnFound && typeof val == 'function') {
                val = this.lv(val, ctx, partials);
            }

            return val;
        },

        // higher order templates
        ho: function(val, cx, partials, text, tags) {
            var t = val.call(cx, text, function(t) {
                return Hogan.compile(t, {
                    delimiters: tags
                }).render(cx, partials);
            });
            var s = Hogan.compile(t.toString(), {
                delimiters: tags
            }).render(cx, partials);
            this.b = s;
            return false;
        },

        // higher order template result buffer
        b: '',

        // lambda replace section
        ls: function(val, ctx, partials, start, end, tags) {
            var cx = ctx[ctx.length - 1],
                t = val.call(cx);

            if (val.length > 0) {
                return this.ho(val, cx, partials, this.text.substring(start, end), tags);
            }

            if (typeof t == 'function') {
                return this.ho(t, cx, partials, this.text.substring(start, end), tags);
            }

            return t;
        },

        // lambda replace variable
        lv: function(val, ctx, partials) {
            var cx = ctx[ctx.length - 1];
            return Hogan.compile(val.call(cx).toString()).render(cx, partials);
        }

    };

    var rAmp = /&/g,
        rLt = /</g,
        rGt = />/g,
        rApos = /\'/g,
        rQuot = /\"/g,
        hChars = /[&<>\"\']/;

    function hoganEscape(str) {
        str = String(str === null ? '' : str);
        return hChars.test(str) ? str.replace(rAmp, '&amp;').replace(rLt, '&lt;').replace(rGt, '&gt;').replace(rApos, '&#39;').replace(rQuot, '&quot;') : str;
    }

    var isArray = Array.isArray ||
    function(a) {
        return Object.prototype.toString.call(a) === '[object Array]';
    };

    return constructor;

})();

var Hogan = (function() {

    // Setup regex  assignments
    // remove whitespace according to Mustache spec
    var rIsWhitespace = /\S/,
        rQuot = /\"/g,
        rNewline = /\n/g,
        rCr = /\r/g,
        rSlash = /\\/g,
        tagTypes = {
            '#': 1,
            '^': 2,
            '/': 3,
            '!': 4,
            '>': 5,
            '<': 6,
            '=': 7,
            '_v': 8,
            '{': 9,
            '&': 10
        };

    function scan(text, delimiters) {
        var len = text.length,
            IN_TEXT = 0,
            IN_TAG_TYPE = 1,
            IN_TAG = 2,
            state = IN_TEXT,
            tagType = null,
            tag = null,
            buf = '',
            tokens = [],
            seenTag = false,
            i = 0,
            lineStart = 0,
            otag = '{{',
            ctag = '}}';

        function addBuf() {
            if (buf.length > 0) {
                tokens.push(new String(buf));
                buf = '';
            }
        }

        function lineIsWhitespace() {
            var isAllWhitespace = true;
            for (var j = lineStart; j < tokens.length; j++) {
                isAllWhitespace = (tokens[j].tag && tagTypes[tokens[j].tag] < tagTypes['_v']) || (!tokens[j].tag && tokens[j].match(rIsWhitespace) === null);
                if (!isAllWhitespace) {
                    return false;
                }
            }

            return isAllWhitespace;
        }

        function filterLine(haveSeenTag, noNewLine) {
            addBuf();

            if (haveSeenTag && lineIsWhitespace()) {
                for (var j = lineStart, next; j < tokens.length; j++) {
                    if (!tokens[j].tag) {
                        if ((next = tokens[j + 1]) && next.tag == '>') {
                            // set indent to token value
                            next.indent = tokens[j].toString()
                        }
                        tokens.splice(j, 1);
                    }
                }
            } else if (!noNewLine) {
                tokens.push({
                    tag: '\n'
                });
            }

            seenTag = false;
            lineStart = tokens.length;
        }

        function changeDelimiters(text, index) {
            var close = '=' + ctag,
                closeIndex = text.indexOf(close, index),
                delimiters = trim(
                text.substring(text.indexOf('=', index) + 1, closeIndex)).split(' ');

            otag = delimiters[0];
            ctag = delimiters[1];

            return closeIndex + close.length - 1;
        }

        if (delimiters) {
            delimiters = delimiters.split(' ');
            otag = delimiters[0];
            ctag = delimiters[1];
        }

        for (i = 0; i < len; i++) {
            if (state == IN_TEXT) {
                if (tagChange(otag, text, i)) {
                    --i;
                    addBuf();
                    state = IN_TAG_TYPE;
                } else {
                    if (text.charAt(i) == '\n') {
                        filterLine(seenTag);
                    } else {
                        buf += text.charAt(i);
                    }
                }
            } else if (state == IN_TAG_TYPE) {
                i += otag.length - 1;
                tag = tagTypes[text.charAt(i + 1)];
                tagType = tag ? text.charAt(i + 1) : '_v';
                if (tagType == '=') {
                    i = changeDelimiters(text, i);
                    state = IN_TEXT;
                } else {
                    if (tag) {
                        i++;
                    }
                    state = IN_TAG;
                }
                seenTag = i;
            } else {
                if (tagChange(ctag, text, i)) {
                    tokens.push({
                        tag: tagType,
                        n: trim(buf),
                        otag: otag,
                        ctag: ctag,
                        i: (tagType == '/') ? seenTag - ctag.length : i + otag.length
                    });
                    buf = '';
                    i += ctag.length - 1;
                    state = IN_TEXT;
                    if (tagType == '{') {
                        i++;
                    }
                } else {
                    buf += text.charAt(i);
                }
            }
        }

        filterLine(seenTag, true);

        return tokens;
    }

    function trim(s) {
        if (s.trim) {
            return s.trim();
        }

        return s.replace(/^\s*|\s*$/g, '');
    }

    function tagChange(tag, text, index) {
        if (text.charAt(index) != tag.charAt(0)) {
            return false;
        }

        for (var i = 1, l = tag.length; i < l; i++) {
            if (text.charAt(index + i) != tag.charAt(i)) {
                return false;
            }
        }

        return true;
    }

    function buildTree(tokens, kind, stack, customTags) {
        var instructions = [],
            opener = null,
            token = null;

        while (tokens.length > 0) {
            token = tokens.shift();
            if (token.tag == '#' || token.tag == '^' || isOpener(token, customTags)) {
                stack.push(token);
                token.nodes = buildTree(tokens, token.tag, stack, customTags);
                instructions.push(token);
            } else if (token.tag == '/') {
                if (stack.length === 0) {
                    throw new Error('Closing tag without opener: /' + token.n);
                }
                opener = stack.pop();
                if (token.n != opener.n && !isCloser(token.n, opener.n, customTags)) {
                    throw new Error('Nesting error: ' + opener.n + ' vs. ' + token.n);
                }
                opener.end = token.i;
                return instructions;
            } else {
                instructions.push(token);
            }
        }

        if (stack.length > 0) {
            throw new Error('missing closing tag: ' + stack.pop().n);
        }

        return instructions;
    }

    function isOpener(token, tags) {
        for (var i = 0, l = tags.length; i < l; i++) {
            if (tags[i].o == token.n) {
                token.tag = '#';
                return true;
            }
        }
    }

    function isCloser(close, open, tags) {
        for (var i = 0, l = tags.length; i < l; i++) {
            if (tags[i].c == close && tags[i].o == open) {
                return true;
            }
        }
    }

    function generate(tree, text, options) {
        var code = 'i = i || "";var c = [cx];var b = i + "";var _ = this;' + walk(tree) + 'return b;';

        if (options.asString) {
            return 'function(cx,p,i){' + code + ';}';
        }

        var template = new HoganTemplate(text);
        template.r = new Function('cx', 'p', 'i', code);
        return template;
    }

    function esc(s) {
        return s.replace(rSlash, '\\\\').replace(rQuot, '\\\"').replace(rNewline, '\\n').replace(rCr, '\\r');
    }

    function chooseMethod(s) {
        return (~s.indexOf('.')) ? 'd' : 'f';
    }

    function walk(tree) {
        var code = '';
        for (var i = 0, l = tree.length; i < l; i++) {
            var tag = tree[i].tag;
            if (tag == '#') {
                code += section(tree[i].nodes, tree[i].n, chooseMethod(tree[i].n), tree[i].i, tree[i].end, tree[i].otag + " " + tree[i].ctag);
            } else if (tag == '^') {
                code += invertedSection(tree[i].nodes, tree[i].n, chooseMethod(tree[i].n));
            } else if (tag == '<' || tag == '>') {
                code += partial(tree[i]);
            } else if (tag == '{' || tag == '&') {
                code += tripleStache(tree[i].n, chooseMethod(tree[i].n));
            } else if (tag == '\n') {
                code += text('"\\n"' + (tree.length - 1 == i ? '' : ' + i'));
            } else if (tag == '_v') {
                code += variable(tree[i].n, chooseMethod(tree[i].n));
            } else if (tag === undefined) {
                code += text('"' + esc(tree[i]) + '"');
            }
        }
        return code;
    }

    function section(nodes, id, method, start, end, tags) {
        return 'if(_.s(_.' + method + '("' + esc(id) + '",c,p,1),' + 'c,p,0,' + start + ',' + end + ', "' + tags + '")){' + 'b += _.rs(c,p,' + 'function(c,p){ var b = "";' + walk(nodes) + 'return b;});c.pop();}' + 'else{b += _.b; _.b = ""};';
    }

    function invertedSection(nodes, id, method) {
        return 'if (!_.s(_.' + method + '("' + esc(id) + '",c,p,1),c,p,1,0,0,"")){' + walk(nodes) + '};';
    }

    function partial(tok) {
        return 'b += _.rp("' + esc(tok.n) + '",c[c.length - 1],p,"' + (tok.indent || '') + '");';
    }

    function tripleStache(id, method) {
        return 'b += (_.' + method + '("' + esc(id) + '",c,p,0));';
    }

    function variable(id, method) {
        return 'b += (_.v(_.' + method + '("' + esc(id) + '",c,p,0)));';
    }

    function text(id) {
        return 'b += ' + id + ';';
    }

    return ({
        scan: scan,

        parse: function(tokens, options) {
            options = options || {};
            return buildTree(tokens, '', [], options.sectionTags || []);
        },

        cache: {},

        compile: function(text, options) {
            // options
            //
            // asString: false (default)
            //
            // sectionTags: [{o: '_foo', c: 'foo'}]
            // An array of object with o and c fields that indicate names for custom
            // section tags. The example above allows parsing of {{_foo}}{{/foo}}.
            //
            // delimiters: A string that overrides the default delimiters.
            // Example: "<% %>"
            //
            options = options || {};

            var t = this.cache[text];

            if (t) {
                return t;
            }

            t = generate(this.parse(scan(text, options.delimiters), options), text, options);
            return this.cache[text] = t;
        }
    });
})();

// Export the hogan constructor for Node.js and CommonJS.
if (typeof module !== 'undefined' && module.exports) {
    module.exports = Hogan;
    module.exports.Template = HoganTemplate;
} else if (typeof define === 'function' && define.amd) {
    define(function() {
        return Hogan;
    });
} else if (typeof exports !== 'undefined') {
    exports.Hogan = Hogan;
    exports.HoganTemplate = HoganTemplate;
}
;
/** @license Hyphenator 4.0.0 - client side hyphenation for webbrowsers
 *  Copyright (C) 2011  Mathias Nater, Zürich (mathias at mnn dot ch)
 *  Project and Source hosted on http://code.google.com/p/hyphenator/
 * 
 *  This JavaScript code is free software: you can redistribute
 *  it and/or modify it under the terms of the GNU Lesser
 *  General Public License (GNU LGPL) as published by the Free Software
 *  Foundation, either version 3 of the License, or (at your option)
 *  any later version.  The code is distributed WITHOUT ANY WARRANTY;
 *  without even the implied warranty of MERCHANTABILITY or FITNESS
 *  FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
 *
 *  As additional permission under GNU GPL version 3 section 7, you
 *  may distribute non-source (e.g., minimized or compacted) forms of
 *  that code without the copy of the GNU GPL normally required by
 *  section 4, provided you include this license notice and a URL
 *  through which recipients can access the Corresponding Source.
 *
 * 
 *  Hyphenator.js contains code from Bram Steins hypher.js-Project:
 *  https://github.com/bramstein/Hypher
 *  
 *  Code from this project is marked in the source and belongs 
 *  to the following license:
 *  
 *  Copyright (c) 2011, Bram Stein
 *  All rights reserved.
 *  
 *  Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions 
 *  are met:
 *   
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer. 
 *   2. Redistributions in binary form must reproduce the above copyright 
 *      notice, this list of conditions and the following disclaimer in the 
 *      documentation and/or other materials provided with the distribution. 
 *   3. The name of the author may not be used to endorse or promote products 
 *      derived from this software without specific prior written permission. 
 *  
 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED 
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
 *  EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
 *  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *  
 */
 
/* 
 *  Comments are jsdoctoolkit formatted. See http://code.google.com/p/jsdoc-toolkit/
 */
 
/* The following comment is for JSLint: */
/*global window, ActiveXObject, unescape */
/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, regexp: true, sub: true, newcap: true, immed: true, evil: true, eqeqeq: false */


/**
 * @constructor
 * @description Provides all functionality to do hyphenation, except the patterns that are loaded
 * externally.
 * @author Mathias Nater, <a href = "mailto:mathias@mnn.ch">mathias@mnn.ch</a>
 * @version X.Y.Z
 * @namespace Holds all methods and properties
 * @example
 * &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
 * &lt;script type = "text/javascript"&gt;
 *   Hyphenator.run();
 * &lt;/script&gt;
 */

var Hyphenator = (function (window) {

	var
	/**
	 * @name Hyphenator-supportedLang
	 * @description
	 * A key-value object that stores supported languages.
	 * The key is the bcp47 code of the language and the value
	 * is the (abbreviated) filename of the pattern file.
	 * @type {Object.<string, string>}
	 * @private
	 * @example
	 * Check if language lang is supported:
	 * if (supportedLang.hasOwnProperty(lang))
	 */
	supportedLang = {
		'be': 'be.js',
		'ca': 'ca.js',
		'cs': 'cs.js',
		'da': 'da.js',
		'bn': 'bn.js',
		'de': 'de.js',
		'el': 'el-monoton.js',
		'el-monoton': 'el-monoton.js',
		'el-polyton': 'el-polyton.js',
		'en': 'en-us.js',
		'en-gb': 'en-gb.js',
		'en-us': 'en-us.js',
		'es': 'es.js',
		'fi': 'fi.js',
		'fr': 'fr.js',
		'grc': 'grc.js',
		'gu': 'gu.js',
		'hi': 'hi.js',
		'hu': 'hu.js',
		'hy': 'hy.js',
		'it': 'it.js',
		'kn': 'kn.js',
		'la': 'la.js',
		'lt': 'lt.js',
		'lv': 'lv.js',
		'ml': 'ml.js',
		'nb': 'nb-no.js',
		'no': 'nb-no.js',
		'nb-no': 'nb-no.js',
		'nl': 'nl.js',
		'or': 'or.js',
		'pa': 'pa.js',
		'pl': 'pl.js',
		'pt': 'pt.js',
		'ru': 'ru.js',
		'sk': 'sk.js',
		'sl': 'sl.js',
		'sv': 'sv.js',
		'ta': 'ta.js',
		'te': 'te.js',
		'tr': 'tr.js',
		'uk': 'uk.js'
	},

	/**
	 * @name Hyphenator-languageHint
	 * @description
	 * An automatically generated string to be displayed in a prompt if the language can't be guessed.
	 * The string is generated using the supportedLang-object.
	 * @see Hyphenator-supportedLang
	 * @type {string}
	 * @private
	 * @see Hyphenator-autoSetMainLanguage
	 */

	languageHint = (function () {
		var k, r = '';
		for (k in supportedLang) {
			if (supportedLang.hasOwnProperty(k)) {
				r += k + ', ';
			}
		}
		r = r.substring(0, r.length - 2);
		return r;
	}()),
	
	/**
	 * @name Hyphenator-prompterStrings
	 * @description
	 * A key-value object holding the strings to be displayed if the language can't be guessed
	 * If you add hyphenation patterns change this string.
	 * @type {Object.<string,string>}
	 * @private
	 * @see Hyphenator-autoSetMainLanguage
	 */	
	prompterStrings = {
		'be': 'Мова гэтага сайта не можа быць вызначаны аўтаматычна. Калі ласка пакажыце мову:',
		'cs': 'Jazyk této internetové stránky nebyl automaticky rozpoznán. Určete prosím její jazyk:',
		'da': 'Denne websides sprog kunne ikke bestemmes. Angiv venligst sprog:',
		'de': 'Die Sprache dieser Webseite konnte nicht automatisch bestimmt werden. Bitte Sprache angeben:',
		'en': 'The language of this website could not be determined automatically. Please indicate the main language:',
		'es': 'El idioma del sitio no pudo determinarse autom%E1ticamente. Por favor, indique el idioma principal:',
		'fi': 'Sivun kielt%E4 ei tunnistettu automaattisesti. M%E4%E4rit%E4 sivun p%E4%E4kieli:',
		'fr': 'La langue de ce site n%u2019a pas pu %EAtre d%E9termin%E9e automatiquement. Veuillez indiquer une langue, s.v.p.%A0:',
		'hu': 'A weboldal nyelvét nem sikerült automatikusan megállapítani. Kérem adja meg a nyelvet:',
		'hy': 'Չհաջողվեց հայտնաբերել այս կայքի լեզուն։ Խնդրում ենք նշեք հիմնական լեզուն՝',
		'it': 'Lingua del sito sconosciuta. Indicare una lingua, per favore:',
		'kn': 'ಜಾಲ ತಾಣದ ಭಾಷೆಯನ್ನು ನಿರ್ಧರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ದಯವಿಟ್ಟು ಮುಖ್ಯ ಭಾಷೆಯನ್ನು ಸೂಚಿಸಿ:',
		'lt': 'Nepavyko automatiškai nustatyti šios svetainės kalbos. Prašome įvesti kalbą:',
		'lv': 'Šīs lapas valodu nevarēja noteikt automātiski. Lūdzu norādiet pamata valodu:',
		'ml': 'ഈ വെ%u0D2C%u0D4D%u200Cസൈറ്റിന്റെ ഭാഷ കണ്ടുപിടിയ്ക്കാ%u0D28%u0D4D%u200D കഴിഞ്ഞില്ല. ഭാഷ ഏതാണെന്നു തിരഞ്ഞെടുക്കുക:',
		'nl': 'De taal van deze website kan niet automatisch worden bepaald. Geef de hoofdtaal op:',
		'no': 'Nettstedets språk kunne ikke finnes automatisk. Vennligst oppgi språk:',
		'pt': 'A língua deste site não pôde ser determinada automaticamente. Por favor indique a língua principal:',
		'ru': 'Язык этого сайта не может быть определен автоматически. Пожалуйста укажите язык:',
		'sl': 'Jezika te spletne strani ni bilo mogoče samodejno določiti. Prosim navedite jezik:',
		'sv': 'Spr%E5ket p%E5 den h%E4r webbplatsen kunde inte avg%F6ras automatiskt. V%E4nligen ange:',
		'tr': 'Bu web sitesinin dili otomatik olarak tespit edilememiştir. Lütfen dökümanın dilini seçiniz%A0:',
		'uk': 'Мова цього веб-сайту не може бути визначена автоматично. Будь ласка, вкажіть головну мову:'
	},
	
	/**
	 * @name Hyphenator-basePath
	 * @description
	 * A string storing the basepath from where Hyphenator.js was loaded.
	 * This is used to load the patternfiles.
	 * The basepath is determined dynamically by searching all script-tags for Hyphenator.js
	 * If the path cannot be determined http://hyphenator.googlecode.com/svn/trunk/ is used as fallback.
	 * @type {string}
	 * @private
	 * @see Hyphenator-loadPatterns
	 */
	basePath = (function () {
		var s = document.getElementsByTagName('script'), i = 0, p, src, t;
		while (!!(t = s[i++])) {
			if (!t.src) {
				continue;
			}
			src = t.src;
			p = src.indexOf('Hyphenator.js');
			if (p !== -1) {
				return src.substring(0, p);
			}
		}
		return 'http://hyphenator.googlecode.com/svn/trunk/';
	}()),

	/**
	 * @name Hyphenator-isLocal
	 * @description
	 * isLocal is true, if Hyphenator is loaded from the same domain, as the webpage, but false, if
	 * it's loaded from an external source (i.e. directly from google.code)
	 */
	isLocal = (function () {
		var re = false;
		if (window.location.href.indexOf(basePath) !== -1) {
			re = true;
		}
		return re;
	}()),
	
	/**
	 * @name Hyphenator-documentLoaded
	 * @description
	 * documentLoaded is true, when the DOM has been loaded. This is set by runOnContentLoaded
	 */
	documentLoaded = false,
	documentCount = 0,
	
	/**
	 * @name Hyphenator-persistentConfig
	 * @description
	 * if persistentConfig is set to true (defaults to false), config options and the state of the 
	 * toggleBox are stored in DOM-storage (according to the storage-setting). So they haven't to be
	 * set for each page.
	 */	
	persistentConfig = false,	

	/**
	 * @name Hyphenator-contextWindow
	 * @description
	 * contextWindow stores the window for the document to be hyphenated.
	 * If there are frames this will change.
	 * So use contextWindow instead of window!
	 */
	contextWindow = window,

	/**
	 * @name Hyphenator-doFrames
	 * @description
	 * switch to control if frames/iframes should be hyphenated, too
	 * defaults to false (frames are a bag of hurt!)
	 */
	doFrames = false,
	
	/**
	 * @name Hyphenator-dontHyphenate
	 * @description
	 * A key-value object containing all html-tags whose content should not be hyphenated
	 * @type {Object.<string,boolean>}
	 * @private
	 * @see Hyphenator-hyphenateElement
	 */
	dontHyphenate = {'script': true, 'code': true, 'pre': true, 'img': true, 'br': true, 'samp': true, 'kbd': true, 'var': true, 'abbr': true, 'acronym': true, 'sub': true, 'sup': true, 'button': true, 'option': true, 'label': true, 'textarea': true, 'input': true, 'math': true, 'svg': true},

	/**
	 * @name Hyphenator-enableCache
	 * @description
	 * A variable to set if caching is enabled or not
	 * @type boolean
	 * @default true
	 * @private
	 * @see Hyphenator.config
	 * @see hyphenateWord
	 */
	enableCache = true,

	/**
	 * @name Hyphenator-storageType
	 * @description
	 * A variable to define what html5-DOM-Storage-Method is used ('none', 'local' or 'session')
	 * @type {string}
	 * @default 'none'
	 * @private
	 * @see Hyphenator.config
	 */	
	storageType = 'local',

	/**
	 * @name Hyphenator-storage
	 * @description
	 * An alias to the storage-Method defined in storageType.
	 * Set by Hyphenator.run()
	 * @type {Object|undefined}
	 * @default null
	 * @private
	 * @see Hyphenator.run
	 */	
	storage,
	
	/**
	 * @name Hyphenator-enableReducedPatternSet
	 * @description
	 * A variable to set if storing the used patterns is set
	 * @type boolean
	 * @default false
	 * @private
	 * @see Hyphenator.config
	 * @see hyphenateWord
	 * @see Hyphenator.getRedPatternSet
	 */	
	enableReducedPatternSet = false,
	
	/**
	 * @name Hyphenator-enableRemoteLoading
	 * @description
	 * A variable to set if pattern files should be loaded remotely or not
	 * @type boolean
	 * @default true
	 * @private
	 * @see Hyphenator.config
	 * @see Hyphenator-loadPatterns
	 */
	enableRemoteLoading = true,
	
	/**
	 * @name Hyphenator-displayToggleBox
	 * @description
	 * A variable to set if the togglebox should be displayed or not
	 * @type boolean
	 * @default false
	 * @private
	 * @see Hyphenator.config
	 * @see Hyphenator-toggleBox
	 */
	displayToggleBox = false,

	/**
	 * @name Hyphenator-css3
	 * @description
	 * A variable to set if css3 hyphenation should be used
	 * @type boolean
	 * @default false
	 * @private
	 * @see Hyphenator.config
	 */
	css3 = false,
	/**
	 * @name Hyphenator-css3_hsupport
	 * @description
	 * A generated object containing information for CSS3-hyphenation support
	 * {
	 *   support: boolean,
	 *   property: <the property name to access hyphen-settings>,
	 *   languages: <an object containing supported languages>
	 * }
	 * @type object
	 * @default undefined
	 * @private
	 * @see Hyphenator-css3_gethsupport
	 */
	css3_h9n,
	/**
	 * @name Hyphenator-css3_gethsupport
	 * @description
	 * This function sets Hyphenator-css3_h9n for the current UA
	 * @type function
	 * @private
	 * @see Hyphenator-css3_h9n
	 */
	css3_gethsupport = function () {
		var s,
		ua = navigator.userAgent,
		r = {
			support: false,
			property: '',
			languages: {}
		};
		if (window.getComputedStyle) {
			s = window.getComputedStyle(window.document.getElementsByTagName('body')[0]);
		} else {
			//ancient Browser don't support CSS3 anyway
			css3_h9n = r;
			return;
		}
		if (ua.indexOf('Chrome') !== -1) {
			//Chrome actually knows -webkit-hyphens but does no hyphenation
			r.support = false;
		} else if ((ua.indexOf('Safari') !== -1) && (s['-webkit-hyphens'] !== undefined)) {
			r.support = true;
			r.property = '-webkit-hyphens';
			if (ua.indexOf('Mobile') !== -1) {
				//iOS only hyphenates in systemlanguage
				r.languages[navigator.language.split('-')[0]] = true;
			} else {
				//Desktop Safari only hyphenates some languages:
				r.languages = {
					de: true,
					en: true,
					es: true,
					fr: true,
					it: true,
					nl: true,
					ru: true,
					zh: true
				};
			}
		} else if ((ua.indexOf('Firefox') !== -1) && (s['MozHyphens'] !== undefined)) {
			r.support = true;
			r.property = 'MozHyphens';
			r.languages = {
				en: true
			};
		}
		css3_h9n = r;
	},
	
	/**
	 * @name Hyphenator-hyphenateClass
	 * @description
	 * A string containing the css-class-name for the hyphenate class
	 * @type {string}
	 * @default 'hyphenate'
	 * @private
	 * @example
	 * &lt;p class = "hyphenate"&gt;Text&lt;/p&gt;
	 * @see Hyphenator.config
	 */
	hyphenateClass = 'hyphenate',

	/**
	 * @name Hyphenator-dontHyphenateClass
	 * @description
	 * A string containing the css-class-name for elements that should not be hyphenated
	 * @type {string}
	 * @default 'donthyphenate'
	 * @private
	 * @example
	 * &lt;p class = "donthyphenate"&gt;Text&lt;/p&gt;
	 * @see Hyphenator.config
	 */
	dontHyphenateClass = 'donthyphenate',
	
	/**
	 * @name Hyphenator-min
	 * @description
	 * A number wich indicates the minimal length of words to hyphenate.
	 * @type {number}
	 * @default 6
	 * @private
	 * @see Hyphenator.config
	 */	
	min = 6,
	
	/**
	 * @name Hyphenator-orphanControl
	 * @description
	 * Control how the last words of a line are handled:
	 * level 1 (default): last word is hyphenated
	 * level 2: last word is not hyphenated
	 * level 3: last word is not hyphenated and last space is non breaking
	 * @type {number}
	 * @default 1
	 * @private
	 */
	orphanControl = 1,
	
	/**
	 * @name Hyphenator-isBookmarklet
	 * @description
	 * Indicates if Hyphanetor runs as bookmarklet or not.
	 * @type boolean
	 * @default false
	 * @private
	 */	
	isBookmarklet = (function () {
		var loc = null, re = false, jsArray = document.getElementsByTagName('script'), i, l;
		for (i = 0, l = jsArray.length; i < l; i++) {
			if (!!jsArray[i].getAttribute('src')) {
				loc = jsArray[i].getAttribute('src');
			}
			if (!loc) {
				continue;
			} else if (loc.indexOf('Hyphenator.js?bm=true') !== -1) {
				re = true;
			}
		}
		return re;
	}()),
	
	/**
	 * @name Hyphenator-mainLanguage
	 * @description
	 * The general language of the document. In contrast to {@link Hyphenator-defaultLanguage},
	 * mainLanguage is defined by the client (i.e. by the html or by a prompt).
	 * @type {string|null}
	 * @private
	 * @see Hyphenator-autoSetMainLanguage
	 */	
	mainLanguage = null,

	/**
	 * @name Hyphenator-defaultLanguage
	 * @description
	 * The language defined by the developper. This language setting is defined by a config option.
	 * It is overwritten by any html-lang-attribute and only taken in count, when no such attribute can
	 * be found (i.e. just before the prompt).
	 * @type {string|null}
	 * @private
	 * @see Hyphenator-autoSetMainLanguage
	 */	
	defaultLanguage = '',
	

	/**
	 * @name Hyphenator-elements
	 * @description
	 * An array holding all elements that have to be hyphenated. This var is filled by
	 * {@link Hyphenator-gatherDocumentInfos}
	 * @type {Array}
	 * @private
	 */	
	elements = (function () {
		var Element = function (element, data) {
			this.element = element;
			this.hyphenated = false;
			this.treated = false; //collected but not hyphenated (dohyphenation is off)
			this.data = data;
		},
		ElementCollection = function () {
			this.count = 0;
			this.hyCount = 0;
			this.list = {};
		};
		ElementCollection.prototype = {
			add: function (el, lang, data) {
				if (!this.list.hasOwnProperty(lang)) {
					this.list[lang] = [];
				}
				this.list[lang].push(new Element(el, data));
				this.count += 1;
			},
			each: function (fn) {
				var k;
				for (k in this.list) {
					if (this.list.hasOwnProperty(k)) {
						fn(k, this.list[k]);
					}
				}
			}
		};
		return new ElementCollection();
	}()),

	
	/**
	 * @name Hyphenator-exceptions
	 * @description
	 * An object containing exceptions as comma separated strings for each language.
	 * When the language-objects are loaded, their exceptions are processed, copied here and then deleted.
	 * @see Hyphenator-prepareLanguagesObj
	 * @type {Object}
	 * @private
	 */	
	exceptions = {},
	
	/**
	 * @name Hyphenator-docLanguages
	 * @description
	 * An object holding all languages used in the document. This is filled by
	 * {@link Hyphenator-gatherDocumentInfos}
	 * @type {Object}
	 * @private
	 */	
	docLanguages = {},

	/**
	 * @name Hyphenator-state
	 * @description
	 * A number that inidcates the current state of the script
	 * 0: not initialized
	 * 1: loading patterns
	 * 2: ready
	 * 3: hyphenation done
	 * 4: hyphenation removed
	 * @type {number}
	 * @private
	 */	
	state = 0,

	/**
	 * @name Hyphenator-url
	 * @description
	 * A string containing a RegularExpression to match URL's
	 * @type {string}
	 * @private
	 */	
	url = '(\\w*:\/\/)?((\\w*:)?(\\w*)@)?((([\\d]{1,3}\\.){3}([\\d]{1,3}))|((www\\.|[a-zA-Z]\\.)?[a-zA-Z0-9\\-\\.]+\\.([a-z]{2,4})))(:\\d*)?(\/[\\w#!:\\.?\\+=&%@!\\-]*)*',
	//      protocoll     usr     pwd                    ip               or                          host                 tld        port               path
	/**
	 * @name Hyphenator-mail
	 * @description
	 * A string containing a RegularExpression to match mail-adresses
	 * @type {string}
	 * @private
	 */	
	mail = '[\\w-\\.]+@[\\w\\.]+',

	/**
	 * @name Hyphenator-urlRE
	 * @description
	 * A RegularExpressions-Object for url- and mail adress matching
	 * @type {RegExp}
	 * @private
	 */		
	urlOrMailRE = new RegExp('(' + url + ')|(' + mail + ')', 'i'),

	/**
	 * @name Hyphenator-zeroWidthSpace
	 * @description
	 * A string that holds a char.
	 * Depending on the browser, this is the zero with space or an empty string.
	 * zeroWidthSpace is used to break URLs
	 * @type {string}
	 * @private
	 */		
	zeroWidthSpace = (function () {
		var zws, ua = navigator.userAgent.toLowerCase();
		zws = String.fromCharCode(8203); //Unicode zero width space
		if (ua.indexOf('msie 6') !== -1) {
			zws = ''; //IE6 doesn't support zws
		}
		if (ua.indexOf('opera') !== -1 && ua.indexOf('version/10.00') !== -1) {
			zws = ''; //opera 10 on XP doesn't support zws
		}
		return zws;
	}()),
	
	/**
	 * @name Hyphenator-createElem
	 * @description
	 * A function alias to document.createElementNS or document.createElement
	 * @type {function(string, Object)}
	 * @private
	 */		
	createElem = function (tagname, context) {
		context = context || contextWindow;
		if (document.createElementNS) {
			return context.document.createElementNS('http://www.w3.org/1999/xhtml', tagname);
		} else if (document.createElement) {
			return context.document.createElement(tagname);
		}
	},
	
	/**
	 * @name Hyphenator-onHyphenationDone
	 * @description
	 * A method to be called, when the last element has been hyphenated or the hyphenation has been
	 * removed from the last element.
	 * @see Hyphenator.config
	 * @type {function()}
	 * @private
	 */		
	onHyphenationDone = function () {},

	/**
	 * @name Hyphenator-onError
	 * @description
	 * A function that can be called upon an error.
	 * @see Hyphenator.config
	 * @type {function(Object)}
	 * @private
	 */		
	onError = function (e) {
		window.alert("Hyphenator.js says:\n\nAn Error ocurred:\n" + e.message);
	},

	/**
	 * @name Hyphenator-selectorFunction
	 * @description
	 * A function that has to return a HTMLNodeList of Elements to be hyphenated.
	 * By default it uses the classname ('hyphenate') to select the elements.
	 * @see Hyphenator.config
	 * @type {function()}
	 * @private
	 */		
	selectorFunction = function () {
		var tmp, el = [], i, l;
		if (document.getElementsByClassName) {
			el = contextWindow.document.getElementsByClassName(hyphenateClass);
		} else {
			tmp = contextWindow.document.getElementsByTagName('*');
			l = tmp.length;
			for (i = 0; i < l; i++)
			{
				if (tmp[i].className.indexOf(hyphenateClass) !== -1 && tmp[i].className.indexOf(dontHyphenateClass) === -1) {
					el.push(tmp[i]);
				}
			}
		}
		return el;
	},

	/**
	 * @name Hyphenator-intermediateState
	 * @description
	 * The value of style.visibility of the text while it is hyphenated.
	 * @see Hyphenator.config
	 * @type {string}
	 * @private
	 */		
	intermediateState = 'hidden',
	
	/**
	 * @name Hyphenator-unhide
	 * @description
	 * How hidden elements unhide: either simultaneous (default: 'wait') or progressively.
	 * 'wait' makes Hyphenator.js to wait until all elements are hyphenated (one redraw)
	 * With 'progressiv' Hyphenator.js unhides elements as soon as they are hyphenated.
	 * @see Hyphenator.config
	 * @type {string}
	 * @private
	 */		
	unhide = 'wait',
	
	/**
	 * @name Hyphenator-hyphen
	 * @description
	 * A string containing the character for in-word-hyphenation
	 * @type {string}
	 * @default the soft hyphen
	 * @private
	 * @see Hyphenator.config
	 */
	hyphen = String.fromCharCode(173),
	
	/**
	 * @name Hyphenator-urlhyphen
	 * @description
	 * A string containing the character for url/mail-hyphenation
	 * @type {string}
	 * @default the zero width space
	 * @private
	 * @see Hyphenator.config
	 * @see Hyphenator-zeroWidthSpace
	 */
	urlhyphen = zeroWidthSpace,

	/**
	 * @name Hyphenator-safeCopy
	 * @description
	 * Defines wether work-around for copy issues is active or not
	 * Not supported by Opera (no onCopy handler)
	 * @type boolean
	 * @default true
	 * @private
	 * @see Hyphenator.config
	 * @see Hyphenator-registerOnCopy
	 */
	safeCopy = true,
	
		
	/*
	 * runOnContentLoaded is based od jQuery.bindReady()
	 * see
	 * jQuery JavaScript Library v1.3.2
	 * http://jquery.com/
	 *
	 * Copyright (c) 2009 John Resig
	 * Dual licensed under the MIT and GPL licenses.
	 * http://docs.jquery.com/License
	 *
	 * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
	 * Revision: 6246
	 */
	/**
	 * @name Hyphenator-runOnContentLoaded
	 * @description
	 * A crossbrowser solution for the DOMContentLoaded-Event based on jQuery
	 * <a href = "http://jquery.com/</a>
	 * I added some functionality: e.g. support for frames and iframes…
	 * @param {Object} w the window-object
	 * @param {function()} f the function to call onDOMContentLoaded
	 * @private
	 */
	runOnContentLoaded = function (w, f) {
		var DOMContentLoaded = function () {}, toplevel, hyphRunForThis = {};
		if (documentLoaded && !hyphRunForThis[w.location.href]) {
			f();
			hyphRunForThis[w.location.href] = true;
			return;
		}
		function init(context) {
			contextWindow = context || window;
			if (!hyphRunForThis[contextWindow.location.href] && (!documentLoaded || contextWindow != window.parent)) {
				documentLoaded = true;
				f();
				hyphRunForThis[contextWindow.location.href] = true;
			}
		}
		
		function doScrollCheck() {
			try {
				// If IE is used, use the trick by Diego Perini
				// http://javascript.nwbox.com/IEContentLoaded/
				document.documentElement.doScroll("left");
			} catch (error) {
				setTimeout(doScrollCheck, 1);
				return;
			}
		
			// and execute any waiting functions
			init(window);
		}

		function doOnLoad() {
			var i, haveAccess, fl = window.frames.length;
			if (doFrames && fl > 0) {
				for (i = 0; i < fl; i++) {
					haveAccess = undefined;
					//try catch isn't enough for webkit
					try {
						//opera throws only on document.toString-access
						haveAccess = window.frames[i].document.toString();
					} catch (e) {
						haveAccess = undefined;
					}
					if (!!haveAccess) {
						init(window.frames[i]);
					}
				}
				contextWindow = window;
				f();
				hyphRunForThis[window.location.href] = true;
			} else {
				init(window);
			}
		}
		
		// Cleanup functions for the document ready method
		if (document.addEventListener) {
			DOMContentLoaded = function () {
				document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
				if (doFrames && window.frames.length > 0) {
					//we are in a frameset, so do nothing but wait for onload to fire
					return;
				} else {
					init(window);
				}
			};
		
		} else if (document.attachEvent) {
			DOMContentLoaded = function () {
				// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
				if (document.readyState === "complete") {
					document.detachEvent("onreadystatechange", DOMContentLoaded);
					if (doFrames && window.frames.length > 0) {
						//we are in a frameset, so do nothing but wait for onload to fire
						return;
					} else {
						init(window);
					}
				}
			};
		}

		// Mozilla, Opera and webkit nightlies currently support this event
		if (document.addEventListener) {
			// Use the handy event callback
			document.addEventListener("DOMContentLoaded", DOMContentLoaded, false);
			
			// A fallback to window.onload, that will always work
			window.addEventListener("load", doOnLoad, false);

		// If IE event model is used
		} else if (document.attachEvent) {
			// ensure firing before onload,
			// maybe late but safe also for iframes
			document.attachEvent("onreadystatechange", DOMContentLoaded);
			
			// A fallback to window.onload, that will always work
			window.attachEvent("onload", doOnLoad);

			// If IE and not a frame
			// continually check to see if the document is ready
			toplevel = false;
			try {
				toplevel = window.frameElement === null;
			} catch (e) {}

			if (document.documentElement.doScroll && toplevel) {
				doScrollCheck();
			}
		}

	},



	/**
	 * @name Hyphenator-getLang
	 * @description
	 * Gets the language of an element. If no language is set, it may use the {@link Hyphenator-mainLanguage}.
	 * @param {Object} el The first parameter is an DOM-Element-Object
	 * @param {boolean} fallback The second parameter is a boolean to tell if the function should return the {@link Hyphenator-mainLanguage}
	 * if there's no language found for the element.
	 * @private
	 */
	getLang = function (el, fallback) {
		if (!!el.getAttribute('lang')) {
			return el.getAttribute('lang').toLowerCase();
		}
		// The following doesn't work in IE due to a bug when getAttribute('xml:lang') in a table
		/*if (!!el.getAttribute('xml:lang')) {
			return el.getAttribute('xml:lang').substring(0, 2);
		}*/
		//instead, we have to do this (thanks to borgzor):
		try {
			if (!!el.getAttribute('xml:lang')) {
				return el.getAttribute('xml:lang').toLowerCase();
			}
		} catch (ex) {}
		if (el.tagName !== 'HTML') {
			return getLang(el.parentNode, true);
		}
		if (fallback) {
			return mainLanguage;
		}
		return null;
	},
	
	/**
	 * @name Hyphenator-autoSetMainLanguage
	 * @description
	 * Retrieves the language of the document from the DOM.
	 * The function looks in the following places:
	 * <ul>
	 * <li>lang-attribute in the html-tag</li>
	 * <li>&lt;meta http-equiv = "content-language" content = "xy" /&gt;</li>
	 * <li>&lt;meta name = "DC.Language" content = "xy" /&gt;</li>
	 * <li>&lt;meta name = "language" content = "xy" /&gt;</li>
	 * </li>
	 * If nothing can be found a prompt using {@link Hyphenator-languageHint} and {@link Hyphenator-prompterStrings} is displayed.
	 * If the retrieved language is in the object {@link Hyphenator-supportedLang} it is copied to {@link Hyphenator-mainLanguage}
	 * @private
	 */		
	autoSetMainLanguage = function (w) {
		w = w || contextWindow;
		var el = w.document.getElementsByTagName('html')[0],
			m = w.document.getElementsByTagName('meta'),
			i, text, e, ul;
		mainLanguage = getLang(el, false);
		if (!mainLanguage) {
			for (i = 0; i < m.length; i++) {
				//<meta http-equiv = "content-language" content="xy">	
				if (!!m[i].getAttribute('http-equiv') && (m[i].getAttribute('http-equiv').toLowerCase() === 'content-language')) {
					mainLanguage = m[i].getAttribute('content').toLowerCase();
				}
				//<meta name = "DC.Language" content="xy">
				if (!!m[i].getAttribute('name') && (m[i].getAttribute('name').toLowerCase() === 'dc.language')) {
					mainLanguage = m[i].getAttribute('content').toLowerCase();
				}			
				//<meta name = "language" content = "xy">
				if (!!m[i].getAttribute('name') && (m[i].getAttribute('name').toLowerCase() === 'language')) {
					mainLanguage = m[i].getAttribute('content').toLowerCase();
				}
			}
		}
		//get lang for frame from enclosing document
		if (!mainLanguage && doFrames && contextWindow != window.parent) {
			autoSetMainLanguage(window.parent);
		}
		//fallback to defaultLang if set
		if (!mainLanguage && defaultLanguage !== '') {
			mainLanguage = defaultLanguage;
		}
		//ask user for lang
		if (!mainLanguage) {
			text = '';
			ul = navigator.language ? navigator.language : navigator.userLanguage;
			ul = ul.substring(0, 2);
			if (prompterStrings.hasOwnProperty(ul)) {
				text = prompterStrings[ul];
			} else {
				text = prompterStrings.en;
			}
			text += ' (ISO 639-1)\n\n' + languageHint;
			mainLanguage = window.prompt(unescape(text), ul).toLowerCase();
		}
		if (!supportedLang.hasOwnProperty(mainLanguage)) {
			if (supportedLang.hasOwnProperty(mainLanguage.split('-')[0])) { //try subtag
				mainLanguage = mainLanguage.split('-')[0];
			} else {
				e = new Error('The language "' + mainLanguage + '" is not yet supported.');
				throw e;
			}
		}
	},
    
	/**
	 * @name Hyphenator-gatherDocumentInfos
	 * @description
	 * This method runs through the DOM and executes the process()-function on:
	 * - every node returned by the {@link Hyphenator-selectorFunction}.
	 * The process()-function copies the element to the elements-variable, sets its visibility
	 * to intermediateState, retrieves its language and recursivly descends the DOM-tree until
	 * the child-Nodes aren't of type 1
	 * @private
	 */		
	gatherDocumentInfos = function () {
		var elToProcess, tmp, i = 0,
		process = function (el, hide, lang) {
			var n, i = 0, hyphenatorSettings = {};

			if (el.lang && typeof(el.lang) === 'string') {
				lang = el.lang.toLowerCase(); //copy attribute-lang to internal lang
			} else if (lang) {
				lang = lang.toLowerCase();
			} else {
				lang = getLang(el, true);
			}
			
			//if css3-hyphenation is supported: use it!
			if (css3 && css3_h9n.support && !!css3_h9n.languages[lang]) {
				el.style[css3_h9n.property] = "auto";
				el.style['-webkit-locale'] = "'" + lang + "'";
			} else {
				if (intermediateState === 'hidden') {
					if (!!el.getAttribute('style')) {
						hyphenatorSettings.hasOwnStyle = true;
					} else {
						hyphenatorSettings.hasOwnStyle = false;					
					}
					hyphenatorSettings.isHidden = true;
					el.style.visibility = 'hidden';
				}
				if (supportedLang[lang]) {
					docLanguages[lang] = true;
				} else {
					if (supportedLang.hasOwnProperty(lang.split('-')[0])) { //try subtag
						lang = lang.split('-')[0];
						hyphenatorSettings.language = lang;
					} else if (!isBookmarklet) {
						onError(new Error('Language ' + lang + ' is not yet supported.'));
					}
				}				
				elements.add(el, lang, hyphenatorSettings);
			}
			while (!!(n = el.childNodes[i++])) {
				if (n.nodeType === 1 && !dontHyphenate[n.nodeName.toLowerCase()] &&
					n.className.indexOf(dontHyphenateClass) === -1 && !(n in elToProcess)) {
					process(n, false, lang);
				}
			}
		};
		if (css3) {
			css3_gethsupport();
		}
		if (isBookmarklet) {
			elToProcess = contextWindow.document.getElementsByTagName('body')[0];
			process(elToProcess, false, mainLanguage);
		} else {
			elToProcess = selectorFunction();
			while (!!(tmp = elToProcess[i++]))
			{
				process(tmp, true, '');
			}			
		}
		if (elements.count === 0) {
			//nothing to hyphenate or all hyphenated b css3
			state = 3;
			onHyphenationDone();
		}
	},
		 
	
	/**
	 * @name Hyphenator-createTrie
	 * @description
	 * converts patterns of the given language in a trie
	 * @private
	 * @param {string} lang the language whose patterns shall be converted
	 */		
	convertPatterns = function (lang) {
		/** @license BSD licenced code
		 * The following code is based on code from hypher.js and adapted for Hyphenator.js
		 * Copyright (c) 2011, Bram Stein
		 */
		var size = 0,
			tree = {
				tpoints: []
			},
			patterns, pattern, i, j, k,
			patternObject = Hyphenator.languages[lang].patterns,
			c, chars, points, t, p, codePoint,
			getPoints = (function () {
				//IE<9 doesn't act like other browsers
				if ('in3se'.split(/\D/).length === 1) {
					return function (pattern) {
						var chars = pattern.split(''), c, i, r = [],
						numb3rs = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}, lastWasNum = false;
						i = 0;
						while (!!(c = chars[i])) {
							if (numb3rs.hasOwnProperty(c)) {
								r.push(c);
								i += 2;
								lastWasNum = true;
							} else {
								r.push('');
								i += 1;
								lastWasNum = false;
							}
						}
						if (!lastWasNum) {
							r.push('');
						}
						return r;
					};
				} else {
					return function (pattern) {
						return pattern.split(/\D/);
					};
				}
			}());
	
		for (size in patternObject) {
			if (patternObject.hasOwnProperty(size)) {
				patterns = patternObject[size].match(new RegExp('.{1,' + (+size) + '}', 'g'));
				i = 0;
				while (!!(pattern = patterns[i++])) {
					chars = pattern.replace(/[\d]/g, '').split('');
					points = getPoints(pattern);
					t = tree;

					j = 0;
					while (!!(c = chars[j++])) {
						codePoint = c.charCodeAt(0);
						
						if (!t[codePoint]) {
							t[codePoint] = {};
						}
						t = t[codePoint];
					}

					t.tpoints = [];
					for (k = 0; k < points.length; k++) {
						p = points[k];
						t.tpoints.push((p == "") ? 0 : p);
					}
				}
			}
		}
		Hyphenator.languages[lang].patterns = tree;
		/**
		 * end of BSD licenced code from hypher.js
		 */
	},

	recreatePattern = function (pattern, nodePoints) {
		var r = [], c = pattern.split(''), i;
		for (i = 0; i < nodePoints.length; i++) {
			if (nodePoints[i] !== 0) {
				r.push(nodePoints[i]);
			}
			if (c[i]) {
				r.push(c[i]);
			}
		}
		return r.join('');
	},
	
	/**
	 * @name Hyphenator-convertExceptionsToObject
	 * @description
	 * Converts a list of comma seprated exceptions to an object:
	 * 'Fortran,Hy-phen-a-tion' -> {'Fortran':'Fortran','Hyphenation':'Hy-phen-a-tion'}
	 * @private
	 * @param {string} exc a comma separated string of exceptions (without spaces)
	 */		
	convertExceptionsToObject = function (exc) {
		var w = exc.split(', '),
			r = {},
			i, l, key;
		for (i = 0, l = w.length; i < l; i++) {
			key = w[i].replace(/-/g, '');
			if (!r.hasOwnProperty(key)) {
				r[key] = w[i];
			}
		}
		return r;
	},
	
	/**
	 * @name Hyphenator-loadPatterns
	 * @description
	 * Adds a &lt;script&gt;-Tag to the DOM to load an externeal .js-file containing patterns and settings for the given language.
	 * If the given language is not in the {@link Hyphenator-supportedLang}-Object it returns.
	 * One may ask why we are not using AJAX to load the patterns. The XMLHttpRequest-Object 
	 * has a same-origin-policy. This makes the isBookmarklet-functionality impossible.
	 * @param {string} lang The language to load the patterns for
	 * @private
	 * @see Hyphenator-basePath
	 */
	loadPatterns = function (lang) {
		var url, xhr, head, script;
		if (supportedLang[lang] && !Hyphenator.languages[lang]) {
	        url = basePath + 'patterns/' + supportedLang[lang];
		} else {
			return;
		}
		if (isLocal && !isBookmarklet) {
			//check if 'url' is available:
			xhr = null;
			if (typeof XMLHttpRequest !== 'undefined') {
				xhr = new XMLHttpRequest();
			}
			if (!xhr) {
				try {
					xhr  = new ActiveXObject("Msxml2.XMLHTTP");
				} catch (e) {
					xhr  = null;
				}
			}
			if (xhr) {
				xhr.open('HEAD', url, false);
				xhr.setRequestHeader('Cache-Control', 'no-cache');
				xhr.send(null);
				if (xhr.status === 404) {
					onError(new Error('Could not load\n' + url));
					delete docLanguages[lang];
					return;
				}
			}
		}
		if (createElem) {
			head = window.document.getElementsByTagName('head').item(0);
			script = createElem('script', window);
			script.src = url;
			script.type = 'text/javascript';
			head.appendChild(script);
		}
	},
	
	/**
	 * @name Hyphenator-prepareLanguagesObj
	 * @description
	 * Adds a cache to each language and converts the exceptions-list to an object.
	 * If storage is active the object is stored there.
	 * @private
	 * @param {string} lang the language ob the lang-obj
	 */		
	prepareLanguagesObj = function (lang) {
		var lo = Hyphenator.languages[lang], wrd;
		if (!lo.prepared) {	
			if (enableCache) {
				lo.cache = {};
				//Export
				lo['cache'] = lo.cache;
			}
			if (enableReducedPatternSet) {
				lo.redPatSet = {};
			}
			//add exceptions from the pattern file to the local 'exceptions'-obj
			if (lo.hasOwnProperty('exceptions')) {
				Hyphenator.addExceptions(lang, lo.exceptions);
				delete lo.exceptions;
			}
			//copy global exceptions to the language specific exceptions
			if (exceptions.hasOwnProperty('global')) {
				if (exceptions.hasOwnProperty(lang)) {
					exceptions[lang] += ', ' + exceptions.global;
				} else {
					exceptions[lang] = exceptions.global;
				}
			}
			//move exceptions from the the local 'exceptions'-obj to the 'language'-object
			if (exceptions.hasOwnProperty(lang)) {
				lo.exceptions = convertExceptionsToObject(exceptions[lang]);
				delete exceptions[lang];
			} else {
				lo.exceptions = {};
			}
			convertPatterns(lang);
			wrd = '[\\w' + lo.specialChars + '@' + String.fromCharCode(173) + String.fromCharCode(8204) + '-]{' + min + ',}';
			lo.genRegExp = new RegExp('(' + url + ')|(' + mail + ')|(' + wrd + ')', 'gi');
			lo.prepared = true;
		}
		if (!!storage) {
			try {
				storage.setItem('Hyphenator_' + lang, window.JSON.stringify(lo));
			} catch (e) {
				//onError(e);
			}
		}
		
	},
	
	/**
	 * @name Hyphenator-prepare
	 * @description
	 * This funtion prepares the Hyphenator-Object: If RemoteLoading is turned off, it assumes
	 * that the patternfiles are loaded, all conversions are made and the callback is called.
	 * If storage is active the object is retrieved there.
	 * If RemoteLoading is on (default), it loads the pattern files and waits until they are loaded,
	 * by repeatedly checking Hyphenator.languages. If a patterfile is loaded the patterns are
	 * converted to their object style and the lang-object extended.
	 * Finally the callback is called.
	 * @private
	 */
	prepare = function (callback) {
		var lang, interval, tmp1, tmp2;
		if (!enableRemoteLoading) {
			for (lang in Hyphenator.languages) {
				if (Hyphenator.languages.hasOwnProperty(lang)) {
					prepareLanguagesObj(lang);
				}
			}
			state = 2;
			callback('*');
			return;
		}
		// get all languages that are used and preload the patterns
		state = 1;
		for (lang in docLanguages) {
			if (docLanguages.hasOwnProperty(lang)) {
				if (!!storage && storage.getItem('Hyphenator_' + lang)) {
					Hyphenator.languages[lang] = window.JSON.parse(storage.getItem('Hyphenator_' + lang));
					if (exceptions.hasOwnProperty('global')) {
						tmp1 = convertExceptionsToObject(exceptions.global);
						for (tmp2 in tmp1) {
							if (tmp1.hasOwnProperty(tmp2)) {
								Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2];
							}
						}
					}
					//Replace exceptions since they may have been changed:
					if (exceptions.hasOwnProperty(lang)) {
						tmp1 = convertExceptionsToObject(exceptions[lang]);
						for (tmp2 in tmp1) {
							if (tmp1.hasOwnProperty(tmp2)) {
								Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2];
							}
						}
						delete exceptions[lang];
					}
					//Replace genRegExp since it may have been changed:
					tmp1 = '[\\w' + Hyphenator.languages[lang].specialChars + '@' + String.fromCharCode(173) + String.fromCharCode(8204) + '-]{' + min + ',}';
					Hyphenator.languages[lang].genRegExp = new RegExp('(' + url + ')|(' + mail + ')|(' + tmp1 + ')', 'gi');
					
					delete docLanguages[lang];
					callback(lang);
					continue;
				} else {
					loadPatterns(lang);
				}
			}
		}
		// else async wait until patterns are loaded, then hyphenate
		interval = window.setInterval(function () {
			var finishedLoading = true, lang;
			for (lang in docLanguages) {
				if (docLanguages.hasOwnProperty(lang)) {
					finishedLoading = false;
					if (!!Hyphenator.languages[lang]) {
						delete docLanguages[lang];
						//do conversion while other patterns are loading:
						prepareLanguagesObj(lang);
						callback(lang);
					}
				}
			}
			if (finishedLoading) {
				//console.log('callig callback for ' + contextWindow.location.href);
				window.clearInterval(interval);
				state = 2;
			}
		}, 100);
	},

	/**
	 * @name Hyphenator-switchToggleBox
	 * @description
	 * Creates or hides the toggleBox: a small button to turn off/on hyphenation on a page.
	 * @see Hyphenator.config
	 * @private
	 */		
	toggleBox = function () {
		var myBox, bdy, myIdAttribute, myTextNode, myClassAttribute,
		text = (Hyphenator.doHyphenation ? 'Hy-phen-a-tion' : 'Hyphenation');
		if (!!(myBox = contextWindow.document.getElementById('HyphenatorToggleBox'))) {
			myBox.firstChild.data = text;
		} else {
			bdy = contextWindow.document.getElementsByTagName('body')[0];
			myBox = createElem('div', contextWindow);
			myIdAttribute = contextWindow.document.createAttribute('id');
			myIdAttribute.nodeValue = 'HyphenatorToggleBox';
			myClassAttribute = contextWindow.document.createAttribute('class');
			myClassAttribute.nodeValue = dontHyphenateClass;
			myTextNode = contextWindow.document.createTextNode(text);
			myBox.appendChild(myTextNode);
			myBox.setAttributeNode(myIdAttribute);
			myBox.setAttributeNode(myClassAttribute);
			myBox.onclick =  Hyphenator.toggleHyphenation;
			myBox.style.position = 'absolute';
			myBox.style.top = '0px';
			myBox.style.right = '0px';
			myBox.style.margin = '0';
			myBox.style.backgroundColor = '#AAAAAA';
			myBox.style.color = '#FFFFFF';
			myBox.style.font = '6pt Arial';
			myBox.style.letterSpacing = '0.2em';
			myBox.style.padding = '3px';
			myBox.style.cursor = 'pointer';
			myBox.style.WebkitBorderBottomLeftRadius = '4px';
			myBox.style.MozBorderRadiusBottomleft = '4px';
			bdy.appendChild(myBox);
		}
	},


	/**
	 * @name Hyphenator-hyphenateWord
	 * @description
	 * This function is the heart of Hyphenator.js. It returns a hyphenated word.
	 *
	 * If there's already a {@link Hyphenator-hypen} in the word, the word is returned as it is.
	 * If the word is in the exceptions list or in the cache, it is retrieved from it.
	 * If there's a '-' put a zeroWidthSpace after the '-' and hyphenate the parts.
	 * @param {string} lang The language of the word
	 * @param {string} word The word
	 * @returns string The hyphenated word
	 * @public
	 */	
	hyphenateWord = function (lang, word) {
		var lo = Hyphenator.languages[lang], parts, l, subst,
			w, characters, originalCharacters, wordLength, i, j, k, node, points = [],
			characterPoints = [], nodePoints, nodePointsLength, m = Math.max, trie,
			result = [''], pattern;
		if (word === '') {
			return '';
		}
		if (word.indexOf(hyphen) !== -1) {
			//word already contains shy; -> leave at it is!
			return word;
		}
		if (enableCache && lo.cache.hasOwnProperty(word)) { //the word is in the cache
			return lo.cache[word];
		}
		if (lo.exceptions.hasOwnProperty(word)) { //the word is in the exceptions list
			return lo.exceptions[word].replace(/-/g, hyphen);
		}
		if (word.indexOf('-') !== -1) {
			//word contains '-' -> hyphenate the parts separated with '-'
			parts = word.split('-');
			for (i = 0, l = parts.length; i < l; i++) {
				parts[i] = hyphenateWord(lang, parts[i]);
			}
			return parts.join('-');
		}
		w = word = '_' + word + '_';
		if (!!lo.charSubstitution) {
			for (subst in lo.charSubstitution) {
				if (lo.charSubstitution.hasOwnProperty(subst)) {
					w = w.replace(new RegExp(subst, 'g'), lo.charSubstitution[subst]);
				}
			}
		}
		if (word.indexOf("'") !== -1) {
			w = w.replace("'", "’"); //replace APOSTROPHE with RIGHT SINGLE QUOTATION MARK (since the latter is used in the patterns)
		}
		/** @license BSD licenced code
		 * The following code is based on code from hypher.js
		 * Copyright (c) 2011, Bram Stein
		 */
		characters = w.toLowerCase().split('');
		originalCharacters = word.split('');
		wordLength = characters.length;
		trie = lo.patterns;
		for (i = 0; i < wordLength; i += 1) {
			points[i] = 0;
			characterPoints[i] = characters[i].charCodeAt(0);
		}
		for (i = 0; i < wordLength; i += 1) {
			pattern = '';
			node = trie;
			for (j = i; j < wordLength; j += 1) {
				node = node[characterPoints[j]];
				if (node) {
					if (enableReducedPatternSet) {
						pattern += String.fromCharCode(characterPoints[j]);
					}
					nodePoints = node.tpoints;
					if (nodePoints) {
						if (enableReducedPatternSet) {
							if (!lo.redPatSet) {
								lo.redPatSet = {};
							}
							lo.redPatSet[pattern] = recreatePattern(pattern, nodePoints);
						}
						for (k = 0, nodePointsLength = nodePoints.length; k < nodePointsLength; k += 1) {
							points[i + k] = m(points[i + k], nodePoints[k]);
						}
					}
				} else {
					break;
				}
			}
		}
		for (i = 1; i < wordLength - 1; i += 1) {
			if (i > lo.leftmin && i < (wordLength - lo.rightmin) && points[i] % 2) {
				result.push(originalCharacters[i]);
			} else {
				result[result.length - 1] += originalCharacters[i];
			}
		}
		return result.join(hyphen);
		/**
		 * end of BSD licenced code from hypher.js
		 */
	},
		
	/**
	 * @name Hyphenator-hyphenateURL
	 * @description
	 * Puts {@link Hyphenator-urlhyphen} after each no-alphanumeric char that my be in a URL.
	 * @param {string} url to hyphenate
	 * @returns string the hyphenated URL
	 * @public
	 */
	hyphenateURL = function (url) {
		return url.replace(/([:\/\.\?#&_,;!@]+)/gi, '$&' + urlhyphen);
	},

	/**
	 * @name Hyphenator-removeHyphenationFromElement
	 * @description
	 * Removes all hyphens from the element. If there are other elements, the function is
	 * called recursively.
	 * Removing hyphens is usefull if you like to copy text. Some browsers are buggy when the copy hyphenated texts.
	 * @param {Object} el The element where to remove hyphenation.
	 * @public
	 */
	removeHyphenationFromElement = function (el) {
		var h, i = 0, n;
		switch (hyphen) {
		case '|':
			h = '\\|';
			break;
		case '+':
			h = '\\+';
			break;
		case '*':
			h = '\\*';
			break;
		default:
			h = hyphen;
		}
		while (!!(n = el.childNodes[i++])) {
			if (n.nodeType === 3) {
				n.data = n.data.replace(new RegExp(h, 'g'), '');
				n.data = n.data.replace(new RegExp(zeroWidthSpace, 'g'), '');
			} else if (n.nodeType === 1) {
				removeHyphenationFromElement(n);
			}
		}
	},
	
	
	/**
	 * @name Hyphenator-registerOnCopy
	 * @description
	 * Huge work-around for browser-inconsistency when it comes to
	 * copying of hyphenated text.
	 * The idea behind this code has been provided by http://github.com/aristus/sweet-justice
	 * sweet-justice is under BSD-License
	 * @private
	 */
	registerOnCopy = function (el) {
		var body = el.ownerDocument.getElementsByTagName('body')[0],
		shadow,
		selection,
		range,
		rangeShadow,
		restore,
		oncopyHandler = function (e) {
			e = e || window.event;
			var target = e.target || e.srcElement,
			currDoc = target.ownerDocument,
			body = currDoc.getElementsByTagName('body')[0],
			targetWindow = 'defaultView' in currDoc ? currDoc.defaultView : currDoc.parentWindow;
			if (target.tagName && dontHyphenate[target.tagName.toLowerCase()]) {
				//Safari needs this
				return;
			}
			//create a hidden shadow element
			shadow = currDoc.createElement('div');
			//Moving the element out of the screen doesn't work for IE9 (https://connect.microsoft.com/IE/feedback/details/663981/)
			//shadow.style.overflow = 'hidden';
			//shadow.style.position = 'absolute';
			//shadow.style.top = '-5000px';
			//shadow.style.height = '1px';
			//doing this instead:
			shadow.style.color = window.getComputedStyle ? targetWindow.getComputedStyle(body).backgroundColor : '#FFFFFF';
			shadow.style.fontSize = '0px';
			body.appendChild(shadow);
			if (!!window.getSelection) {
				//FF3, Webkit, IE9
				e.stopPropagation();
				selection = targetWindow.getSelection();
				range = selection.getRangeAt(0);
				shadow.appendChild(range.cloneContents());
				removeHyphenationFromElement(shadow);
				selection.selectAllChildren(shadow);
				restore = function () {
					shadow.parentNode.removeChild(shadow);
					selection.removeAllRanges(); //IE9 needs that
					selection.addRange(range);
				};
			} else {
				// IE<9
				e.cancelBubble = true;
				selection = targetWindow.document.selection;
				range = selection.createRange();
				shadow.innerHTML = range.htmlText;
				removeHyphenationFromElement(shadow);
				rangeShadow = body.createTextRange();
				rangeShadow.moveToElementText(shadow);
				rangeShadow.select();
				restore = function () {
					shadow.parentNode.removeChild(shadow);
					if (range.text !== "") {
						range.select();
					}
				};
			}
			window.setTimeout(restore, 0);
		};
		if (!body) {
			return;
		}
		el = el || body;
		if (window.addEventListener) {
			el.addEventListener("copy", oncopyHandler, true);
		} else {
			el.attachEvent("oncopy", oncopyHandler);
		}
	},
	
	/**
	 * @name Hyphenator-unhideElement
	 * @description
	 * Unhides an element and removes the visibility attr if set by hyphenator
	 * @param Object The Element object from ElementCollection
	 * @private
	 */	
	unhideElement = function (elo) {
		var el = elo.element,
		hyphenatorSettings = elo.data;
		el.style.visibility = 'visible';
		elo.data.isHidden = false;
		if (!hyphenatorSettings.hasOwnStyle) {
			el.setAttribute('style', ''); // without this, removeAttribute doesn't work in Safari (thanks to molily)
			el.removeAttribute('style');
		} else {
			if (el.style.removeProperty) {
				el.style.removeProperty('visibility');
			} else if (el.style.removeAttribute) { // IE
				el.style.removeAttribute('visibility');
			}  
		}
	},

	/**
	 * @name Hyphenator-checkIfAllDone
	 * @description
	 * Checks if all Elements are hyphenated, unhides them and fires onHyphenationDone()
	 * @private
	 */		
	checkIfAllDone = function () {
		var allDone = true;
		elements.each(function (lang, list) {
			var i, l = list.length;
			for (i = 0; i < l; i++) {
				allDone = allDone && list[i].hyphenated;
				if (intermediateState === 'hidden' && unhide === 'wait') {
					unhideElement(list[i]);
				}
			}
		});
		if (allDone) {
			state = 3;
			onHyphenationDone();
		}
	},


	/**
	 * @name Hyphenator-hyphenateElement
	 * @description
	 * Takes the content of the given element and - if there's text - replaces the words
	 * by hyphenated words. If there's another element, the function is called recursively.
	 * When all words are hyphenated, the visibility of the element is set to 'visible'.
	 * @param {Object} el The element to hyphenate
	 * @private
	 */
	hyphenateElement = function (lang, elo) {
		var hyphenatorSettings = elo.data,
			el = elo.element,
			hyphenate, n, i,
			controlOrphans = function (part) {
				var h, r;
				switch (hyphen) {
				case '|':
					h = '\\|';
					break;
				case '+':
					h = '\\+';
					break;
				case '*':
					h = '\\*';
					break;
				default:
					h = hyphen;
				}
				if (orphanControl >= 2) {
					//remove hyphen points from last word
					r = part.split(' ');
					r[1] = r[1].replace(new RegExp(h, 'g'), '');
					r[1] = r[1].replace(new RegExp(zeroWidthSpace, 'g'), '');
					r = r.join(' ');
				}
				if (orphanControl === 3) {
					//replace spaces by non breaking spaces
					r = r.replace(/[ ]+/g, String.fromCharCode(160));
				}
				return r;
			};
		if (Hyphenator.languages.hasOwnProperty(lang)) {
			hyphenate = function (word) {
				if (!Hyphenator.doHyphenation) {
					return word;
				} else if (urlOrMailRE.test(word)) {
					return hyphenateURL(word);
				} else {
					return hyphenateWord(lang, word);
				}
			};
			if (safeCopy && (el.tagName.toLowerCase() !== 'body')) {
				registerOnCopy(el);
			}
			i = 0;
			while (!!(n = el.childNodes[i++])) {
				if (n.nodeType === 3 && n.data.length >= min) { //type 3 = #text -> hyphenate!
					n.data = n.data.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
					if (orphanControl !== 1) {
						n.data = n.data.replace(/[\S]+ [\S]+$/, controlOrphans);
					}
				}
			}
		}
		if (hyphenatorSettings.isHidden && intermediateState === 'hidden' && unhide === 'progressive') {
			unhideElement(elo);
		}
		elo.hyphenated = true;
		elements.hyCount += 1;
		if (elements.count <= elements.hyCount) {
			checkIfAllDone();
		}
	},
	

	/**
	 * @name Hyphenator-hyphenateLanguageElements
	 * @description
	 * Calls hyphenateElement() for all elements of the specified language.
	 * If the language is '*' then all elements are hyphenated.
	 * This is done with a setTimout
	 * to prevent a "long running Script"-alert when hyphenating large pages.
	 * Therefore a tricky bind()-function was necessary.
	 * @private
	 */
	hyphenateLanguageElements = function (lang) {
		function bind(fun, arg1, arg2) {
			return function () {
				return fun(arg1, arg2);
			};
		}
		var el, i, l;
		if (lang === '*') {
			elements.each(function (lang, langels) {
				var i, l = langels.length;
				for (i = 0; i < l; i++) {
					window.setTimeout(bind(hyphenateElement, lang, langels[i]), 0);
				}
			});
		} else {
			if (elements.list.hasOwnProperty(lang)) {
				l = elements.list[lang].length;
				for (i = 0; i < l; i++) {
					window.setTimeout(bind(hyphenateElement, lang, elements.list[lang][i]), 0);
				}
			}
		}
	},
	
	/**
	 * @name Hyphenator-removeHyphenationFromDocument
	 * @description
	 * Does what it says ;-)
	 * @private
	 */
	removeHyphenationFromDocument = function () {
		elements.each(function (lang, elo) {
			var i, l = elo.length, el;
			for (i = 0; i < l; i++) {
				removeHyphenationFromElement(elo[i].element);
				elo[i].hyphenated = false;
			}
		});
		state = 4;
	},
		
	/**
	 * @name Hyphenator-createStorage
	 * @description
	 * inits the private var storage depending of the setting in storageType
	 * and the supported features of the system.
	 * @private
	 */
	createStorage = function () {
		try {
			if (storageType !== 'none' &&
				typeof(window.localStorage) !== 'undefined' &&
				typeof(window.sessionStorage) !== 'undefined' &&
				typeof(window.JSON.stringify) !== 'undefined' &&
				typeof(window.JSON.parse) !== 'undefined') {
				switch (storageType) {
				case 'session':
					storage = window.sessionStorage;
					break;
				case 'local':
					storage = window.localStorage;
					break;
				default:
					storage = undefined;
					break;
				}
			}
		} catch (f) {
			//FF throws an error if DOM.storage.enabled is set to false
		}
	},
	
	/**
	 * @name Hyphenator-storeConfiguration
	 * @description
	 * Stores the current config-options in DOM-Storage
	 * @private
	 */
	storeConfiguration = function () {
		if (!storage) {
			return;
		}
		var settings = {
			'STORED': true,
			'classname': hyphenateClass,
			'donthyphenateclassname': dontHyphenateClass,
			'minwordlength': min,
			'hyphenchar': hyphen,
			'urlhyphenchar': urlhyphen,
			'togglebox': toggleBox,
			'displaytogglebox': displayToggleBox,
			'remoteloading': enableRemoteLoading,
			'enablecache': enableCache,
			'onhyphenationdonecallback': onHyphenationDone,
			'onerrorhandler': onError,
			'intermediatestate': intermediateState,
			'selectorfunction': selectorFunction,
			'safecopy': safeCopy,
			'doframes': doFrames,
			'storagetype': storageType,
			'orphancontrol': orphanControl,
			'dohyphenation': Hyphenator.doHyphenation,
			'persistentconfig': persistentConfig,
			'defaultlanguage': defaultLanguage
		};
		storage.setItem('Hyphenator_config', window.JSON.stringify(settings));
	},
	
	/**
	 * @name Hyphenator-restoreConfiguration
	 * @description
	 * Retrieves config-options from DOM-Storage and does configuration accordingly
	 * @private
	 */
	restoreConfiguration = function () {
		var settings;
		if (storage.getItem('Hyphenator_config')) {
			settings = window.JSON.parse(storage.getItem('Hyphenator_config'));
			Hyphenator.config(settings);
		}
	};

	return {
		
		/**
		 * @name Hyphenator.version
		 * @memberOf Hyphenator
		 * @description
		 * String containing the actual version of Hyphenator.js
		 * [major release].[minor releas].[bugfix release]
		 * major release: new API, new Features, big changes
		 * minor release: new languages, improvements
		 * @public
         */		
		version: '4.0.0',

		/**
		 * @name Hyphenator.doHyphenation
		 * @description
		 * If doHyphenation is set to false (defaults to true), hyphenateDocument() isn't called.
		 * All other actions are performed.
		 */		
		doHyphenation: true,
		
		/**
		 * @name Hyphenator.languages
		 * @memberOf Hyphenator
		 * @description
		 * Objects that holds key-value pairs, where key is the language and the value is the
		 * language-object loaded from (and set by) the pattern file.
		 * The language object holds the following members:
		 * <table>
		 * <tr><th>key</th><th>desc></th></tr>
		 * <tr><td>leftmin</td><td>The minimum of chars to remain on the old line</td></tr>
		 * <tr><td>rightmin</td><td>The minimum of chars to go on the new line</td></tr>
		 * <tr><td>shortestPattern</td><td>The shortes pattern (numbers don't count!)</td></tr>
		 * <tr><td>longestPattern</td><td>The longest pattern (numbers don't count!)</td></tr>
		 * <tr><td>specialChars</td><td>Non-ASCII chars in the alphabet.</td></tr>
		 * <tr><td>patterns</td><td>the patterns</td></tr>
		 * </table>
		 * And optionally (or after prepareLanguagesObj() has been called):
		 * <table>
		 * <tr><td>exceptions</td><td>Excpetions for the secified language</td></tr>
		 * </table>
		 * @public
         */		
		languages: {},
		

		/**
		 * @name Hyphenator.config
			 * @description
		 * Config function that takes an object as an argument. The object contains key-value-pairs
		 * containig Hyphenator-settings. This is a shortcut for calling Hyphenator.set...-Methods.
		 * @param {Object} obj <table>
		 * <tr><th>key</th><th>values</th><th>default</th></tr>
		 * <tr><td>classname</td><td>string</td><td>'hyphenate'</td></tr>
		 * <tr><td>donthyphenateclassname</td><td>string</td><td>''</td></tr>
		 * <tr><td>minwordlength</td><td>integer</td><td>6</td></tr>
		 * <tr><td>hyphenchar</td><td>string</td><td>'&amp;shy;'</td></tr>
		 * <tr><td>urlhyphenchar</td><td>string</td><td>'zero with space'</td></tr>
		 * <tr><td>togglebox</td><td>function</td><td>see code</td></tr>
		 * <tr><td>displaytogglebox</td><td>boolean</td><td>false</td></tr>
		 * <tr><td>remoteloading</td><td>boolean</td><td>true</td></tr>
		 * <tr><td>enablecache</td><td>boolean</td><td>true</td></tr>
		 * <tr><td>enablereducedpatternset</td><td>boolean</td><td>false</td></tr>
		 * <tr><td>onhyphenationdonecallback</td><td>function</td><td>empty function</td></tr>
		 * <tr><td>onerrorhandler</td><td>function</td><td>alert(onError)</td></tr>
		 * <tr><td>intermediatestate</td><td>string</td><td>'hidden'</td></tr>
		 * <tr><td>selectorfunction</td><td>function</td><td>[…]</td></tr>
		 * <tr><td>safecopy</td><td>boolean</td><td>true</td></tr>
		 * <tr><td>doframes</td><td>boolean</td><td>false</td></tr>
		 * <tr><td>storagetype</td><td>string</td><td>'none'</td></tr>
		 * </table>
		 * @public
		 * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
         * &lt;script type = "text/javascript"&gt;
         *     Hyphenator.config({'minwordlength':4,'hyphenchar':'|'});
         *     Hyphenator.run();
         * &lt;/script&gt;
         */
		config: function (obj) {
			var assert = function (name, type) {
					if (typeof obj[name] === type) {
						return true;
					} else {
						onError(new Error('Config onError: ' + name + ' must be of type ' + type));
						return false;
					}
				},
				key;

			if (obj.hasOwnProperty('storagetype')) {
				if (assert('storagetype', 'string')) {
					storageType = obj.storagetype;
				}
				if (!storage) {
					createStorage();
				}			
			}
			if (!obj.hasOwnProperty('STORED') && storage && obj.hasOwnProperty('persistentconfig') && obj.persistentconfig === true) {
				restoreConfiguration();
			}
			
			for (key in obj) {
				if (obj.hasOwnProperty(key)) {
					switch (key) {
					case 'STORED':
						break;
					case 'classname':
						if (assert('classname', 'string')) {
							hyphenateClass = obj[key];
						}
						break;
					case 'donthyphenateclassname':
						if (assert('donthyphenateclassname', 'string')) {
							dontHyphenateClass = obj[key];
						}						
						break;
					case 'minwordlength':
						if (assert('minwordlength', 'number')) {
							min = obj[key];
						}
						break;
					case 'hyphenchar':
						if (assert('hyphenchar', 'string')) {
							if (obj.hyphenchar === '&shy;') {
								obj.hyphenchar = String.fromCharCode(173);
							}
							hyphen = obj[key];
						}
						break;
					case 'urlhyphenchar':
						if (obj.hasOwnProperty('urlhyphenchar')) {
							if (assert('urlhyphenchar', 'string')) {
								urlhyphen = obj[key];
							}
						}
						break;
					case 'togglebox':
						if (assert('togglebox', 'function')) {
							toggleBox = obj[key];
						}
						break;
					case 'displaytogglebox':
						if (assert('displaytogglebox', 'boolean')) {
							displayToggleBox = obj[key];
						}
						break;
					case 'remoteloading':
						if (assert('remoteloading', 'boolean')) {
							enableRemoteLoading = obj[key];
						}
						break;
					case 'enablecache':
						if (assert('enablecache', 'boolean')) {
							enableCache = obj[key];
						}
						break;
					case 'enablereducedpatternset':
						if (assert('enablereducedpatternset', 'boolean')) {
							enableReducedPatternSet = obj[key];
						}
						break;
					case 'onhyphenationdonecallback':
						if (assert('onhyphenationdonecallback', 'function')) {
							onHyphenationDone = obj[key];
						}
						break;
					case 'onerrorhandler':
						if (assert('onerrorhandler', 'function')) {
							onError = obj[key];
						}
						break;
					case 'intermediatestate':
						if (assert('intermediatestate', 'string')) {
							intermediateState = obj[key];
						}
						break;
					case 'selectorfunction':
						if (assert('selectorfunction', 'function')) {
							selectorFunction = obj[key];
						}
						break;
					case 'safecopy':
						if (assert('safecopy', 'boolean')) {
							safeCopy = obj[key];
						}
						break;
					case 'doframes':
						if (assert('doframes', 'boolean')) {
							doFrames = obj[key];
						}
						break;
					case 'storagetype':
						if (assert('storagetype', 'string')) {
							storageType = obj[key];
						}						
						break;
					case 'orphancontrol':
						if (assert('orphancontrol', 'number')) {
							orphanControl = obj[key];
						}
						break;
					case 'dohyphenation':
						if (assert('dohyphenation', 'boolean')) {
							Hyphenator.doHyphenation = obj[key];
						}
						break;
					case 'persistentconfig':
						if (assert('persistentconfig', 'boolean')) {
							persistentConfig = obj[key];
						}
						break;
					case 'defaultlanguage':
						if (assert('defaultlanguage', 'string')) {
							defaultLanguage = obj[key];
						}
						break;
					case 'useCSS3hyphenation':
						if (assert('useCSS3hyphenation', 'boolean')) {
							css3 = obj[key];
						}
						break;
					case 'unhide':
						if (assert('unhide', 'string')) {
							unhide = obj[key];
						}
						break;
					default:
						onError(new Error('Hyphenator.config: property ' + key + ' not known.'));
					}
				}
			}
			if (storage && persistentConfig) {
				storeConfiguration();
			}
		},

		/**
		 * @name Hyphenator.run
			 * @description
		 * Bootstrap function that starts all hyphenation processes when called.
		 * @public
		 * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
         * &lt;script type = "text/javascript"&gt;
         *   Hyphenator.run();
         * &lt;/script&gt;
         */
		run: function () {
			documentCount = 0;
			var process = function () {
				try {
					if (contextWindow.document.getElementsByTagName('frameset').length > 0) {
						return; //we are in a frameset
					}
					documentCount++;
					autoSetMainLanguage(undefined);
					gatherDocumentInfos();
					//console.log('preparing for ' + contextWindow.location.href);
					prepare(hyphenateLanguageElements);
					if (displayToggleBox) {
						toggleBox();
					}
				} catch (e) {
					onError(e);
				}
			}, i, haveAccess, fl = window.frames.length;
			
			if (!storage) {
				createStorage();
			}
			if (!documentLoaded && !isBookmarklet) {
				runOnContentLoaded(window, process);
			}
			if (isBookmarklet || documentLoaded) {
				if (doFrames && fl > 0) {
					for (i = 0; i < fl; i++) {
						haveAccess = undefined;
						//try catch isn't enough for webkit
						try {
							//opera throws only on document.toString-access
							haveAccess = window.frames[i].document.toString();
						} catch (e) {
							haveAccess = undefined;
						}
						if (!!haveAccess) {
							contextWindow = window.frames[i];
							process();
						}						
					}
				}
				contextWindow = window;
				process();
			}
		},
		
		/**
		 * @name Hyphenator.addExceptions
			 * @description
		 * Adds the exceptions from the string to the appropriate language in the 
		 * {@link Hyphenator-languages}-object
		 * @param {string} lang The language
		 * @param {string} words A comma separated string of hyphenated words WITH spaces.
		 * @public
		 * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
         * &lt;script type = "text/javascript"&gt;
         *   Hyphenator.addExceptions('de','ziem-lich, Wach-stube');
         *   Hyphenator.run();
         * &lt;/script&gt;
         */
		addExceptions: function (lang, words) {
			if (lang === '') {
				lang = 'global';
			}
			if (exceptions.hasOwnProperty(lang)) {
				exceptions[lang] += ", " + words;
			} else {
				exceptions[lang] = words;
			}
		},
		
		/**
		 * @name Hyphenator.hyphenate
			 * @public
		 * @description
		 * Hyphenates the target. The language patterns must be loaded.
		 * If the target is a string, the hyphenated string is returned,
		 * if it's an object, the values are hyphenated directly.
		 * @param {string|Object} target the target to be hyphenated
		 * @param {string} lang the language of the target
		 * @returns string
		 * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
		 * &lt;script src = "patterns/en.js" type = "text/javascript"&gt;&lt;/script&gt;
         * &lt;script type = "text/javascript"&gt;
		 * var t = Hyphenator.hyphenate('Hyphenation', 'en'); //Hy|phen|ation
		 * &lt;/script&gt;
		 */
		hyphenate: function (target, lang) {
			var hyphenate, n, i;
			if (Hyphenator.languages.hasOwnProperty(lang)) {
				if (!Hyphenator.languages[lang].prepared) {
					prepareLanguagesObj(lang);
				}
				hyphenate = function (word) {
					if (urlOrMailRE.test(word)) {
						return hyphenateURL(word);
					} else {
						return hyphenateWord(lang, word);
					}
				};
				if (typeof target === 'string' || target.constructor === String) {
					return target.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
				} else if (typeof target === 'object') {
					i = 0;
					while (!!(n = target.childNodes[i++])) {
						if (n.nodeType === 3 && n.data.length >= min) { //type 3 = #text -> hyphenate!
							n.data = n.data.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
						} else if (n.nodeType === 1) {
							if (n.lang !== '') {
								Hyphenator.hyphenate(n, n.lang);
							} else {
								Hyphenator.hyphenate(n, lang);
							}
						}
					}
				}
			} else {
				onError(new Error('Language "' + lang + '" is not loaded.'));
			}
		},
		
		/**
		 * @name Hyphenator.getRedPatternSet
			 * @description
		 * Returns {@link Hyphenator-isBookmarklet}.
		 * @param {string} lang the language patterns are stored for
		 * @returns object {'patk': pat}
		 * @public
         */
		getRedPatternSet: function (lang) {
			return Hyphenator.languages[lang].redPatSet;
		},
		
		/**
		 * @name Hyphenator.isBookmarklet
			 * @description
		 * Returns {@link Hyphenator-isBookmarklet}.
		 * @returns boolean
		 * @public
         */
		isBookmarklet: function () {
			return isBookmarklet;
		},

		getConfigFromURI: function () {
			var loc = null, re = {}, jsArray = document.getElementsByTagName('script'), i, j, l, s, gp, option;
			for (i = 0, l = jsArray.length; i < l; i++) {
				if (!!jsArray[i].getAttribute('src')) {
					loc = jsArray[i].getAttribute('src');
				}
				if (!loc) {
					continue;
				} else {
					s = loc.indexOf('Hyphenator.js?');
					if (s === -1) {
						continue;
					}
					gp = loc.substring(s + 14).split('&');
					for (j = 0; j < gp.length; j++) {
						option = gp[j].split('=');
						if (option[0] === 'bm') {
							continue;
						}
						if (option[1] === 'true') {
							re[option[0]] = true;
							continue;
						}
						if (option[1] === 'false') {
							re[option[0]] = false;
							continue;
						}
						if (isFinite(option[1])) {
							re[option[0]] = parseInt(option[1], 10);
							continue;
						}
						if (option[0] === 'onhyphenationdonecallback') {
							re[option[0]] = new Function('', option[1]);
							continue;
						}
						re[option[0]] = option[1];
					}
					break;
				}
			}
			return re;
		},

		/**
		 * @name Hyphenator.toggleHyphenation
			 * @description
		 * Checks the current state of the ToggleBox and removes or does hyphenation.
		 * @public
         */
		toggleHyphenation: function () {
			if (Hyphenator.doHyphenation) {
				removeHyphenationFromDocument();
				Hyphenator.doHyphenation = false;
				storeConfiguration();
				toggleBox();
			} else {
				hyphenateLanguageElements('*');
				Hyphenator.doHyphenation = true;
				storeConfiguration();
				toggleBox();
			}
		}
	};
}(window));

//Export properties/methods (for google closure compiler)
Hyphenator['languages'] = Hyphenator.languages;
Hyphenator['config'] = Hyphenator.config;
Hyphenator['run'] = Hyphenator.run;
Hyphenator['addExceptions'] = Hyphenator.addExceptions;
Hyphenator['hyphenate'] = Hyphenator.hyphenate;
Hyphenator['getRedPatternSet'] = Hyphenator.getRedPatternSet;
Hyphenator['isBookmarklet'] = Hyphenator.isBookmarklet;
Hyphenator['getConfigFromURI'] = Hyphenator.getConfigFromURI;
Hyphenator['toggleHyphenation'] = Hyphenator.toggleHyphenation;
window['Hyphenator'] = Hyphenator;

if (Hyphenator.isBookmarklet()) {
	Hyphenator.config({displaytogglebox: true, intermediatestate: 'visible', doframes: true});
	Hyphenator.config(Hyphenator.getConfigFromURI());
	Hyphenator.run();
}
;
Hyphenator.languages['it'] = {
	leftmin : 2,
	rightmin : 2,
	specialChars : "àéèìòù’'",
	// The italian hyphenation patterns are retrieved from 
	// http://www.ctan.org/tex-archive/language/hyphenation/ithyph.tex
	patterns : {
		2 : "1b1c1d1f1g1h1j1k1l1m1n1p1q1r1t1v1w1x1z",
		3 : "2’2e2w2bb2bc2bd2bf2bm2bn2bp2bs2bt2bvb2lb2r2b_2b’2cb2cc2cd2cf2ck2cm2cn2cq2cs2ct2czc2hc2lc2r2c_2c’_c22db2dd2dg2dl2dm2dn2dpd2r2ds2dt2dv2dw2d_2d’_d22fb2fg2ff2fnf2lf2r2fs2ft2f_2f’2gb2gd2gf2ggg2hg2l2gmg2n2gpg2r2gs2gt2gv2gw2gz2g_2g’2hb2hd2hhh2l2hm2hn2hr2hv2h_2h’2j_2j’2kg2kfk2h2kkk2l2kmk2r2ks2kt2k_2k’2lb2lc2ld2lgl2h2lk2ll2lm2ln2lp2lq2lr2ls2lt2lv2lw2lz2l_2mb2mc2mf2ml2mm2mn2mp2mq2mr2ms2mt2mv2mw2m_2m’2nb2nc2nd2nf2ng2nk2nl2nm2nn2np2nq2nr2ns2nt2nv2nz2n_2n’2pdp2hp2l2pn2ppp2r2ps2pt2pz2p_2p’2qq2q_2q’2rb2rc2rd2rfr2h2rg2rk2rl2rm2rn2rp2rq2rr2rs2rt2rv2rx2rw2rz2r_2r’1s22sz4s_2tb2tc2td2tf2tgt2ht2l2tm2tn2tpt2rt2s2tt2tv2twt2z2t_2vcv2lv2r2vv2v_w2h2w_2w’2xb2xc2xf2xh2xm2xp2xt2xw2x_2x’y1i2zb2zd2zl2zn2zp2zt2zs2zv2zz2z_",
		4 : "_p2sa1iaa1iea1ioa1iua1uoa1ya2at_e1iuo1iao1ieo1ioo1iu2chh2chbch2r2chn2l’_2l’’2shm2sh_2sh’2s3s2stb2stc2std2stf2stg2stm2stn2stp2sts2stt2stv4s’_4s’’2tzktz2s2t’_2t’’2v’_2v’’wa2r2w1yy1ou2z’_2z’’_z2",
		5 : "_bio1_pre12gh2t2l3f2n2g3n3p2nes4s3mt2t3s",
		6 : "_a3p2n_anti1_free3_opto1_para1hi3p2n2nheit3p2sicr2t2s32s3p2n3t2sch",
		7 : "_ca4p3s_e2x1eu_narco1_su2b3r_wa2g3n_wel2t1n2s3fer",
		8 : "_contro1_fran2k3_li3p2sa_orto3p2_poli3p2_sha2re3_su2b3lu",
		9 : "_anti3m2n_circu2m1_re1i2scr_tran2s3c_tran2s3d_tran2s3l_tran2s3n_tran2s3p_tran2s3r_tran2s3t",
		10 : "_di2s3cine"
	}
};
// Infinite Scroll
(function($) {
    $.fn.infinitescroll = function(options) {
        return $(this).each(function() {
            var el = $(this);
            var settings = $.extend({
                url: null,
                triggerAt: 300,
                page: 2,
                appendTo: '.list tbody',
                container: $(document)
            }, options);
            var req = null;
            var maxReached = false;

            var infinityRunner = function() {
                if (settings.url !== null) {
                    if (settings.force || (settings.triggerAt >= (settings.container.height() - el.height() - el.scrollTop()))) {
                        settings.force = false;
                        // if the request is in progress, exit and wait for it to finish
                        if (req && req.readyState < 4 && req.readyState > 0) {
                            return;
                        }
                        $(settings.appendTo).trigger('infinitescroll.beforesend');
                        req = $.get(settings.url.replace("{{page}}", settings.page), function(data) {
                            if (data !== '') {
                                if (settings.page > 1) {
                                    $(settings.appendTo).append(data);
                                } else {
                                    $(settings.appendTo).html(data);
                                }
                                settings.page++;
                                $(settings.appendTo).trigger('infinitescroll.finish');
                            } else {
                                maxReached = true;
                                $(settings.appendTo).trigger('infinitescroll.maxreached');
                            }
                        }, 'html').error(function() {
                          maxReached = true;
                          $(settings.appendTo).trigger('infinitescroll.maxreached');
                        });
                    }
                }
            };

            el.bind('infinitescroll.scrollpage', function(e, page) {
                settings.page = page;
                settings.force = true;
                infinityRunner();
            });

            el.scroll(function(e) {
                if (!maxReached) {
                    infinityRunner();
                }
            });

            // Test initial page layout for trigger
            infinityRunner();
        });
    };
})(jQuery);
/*!
 *
 * Portamento  v1.1.1 - 2011-09-02
 * http://simianstudios.com/portamento
 *
 * Copyright 2011 Kris Noble except where noted.
 *
 * Dual-licensed under the GPLv3 and Apache 2.0 licenses:
 * http://www.gnu.org/licenses/gpl-3.0.html
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 */
/**
 *
 * Creates a sliding panel that respects the boundaries of
 * a given wrapper, and also has sensible behaviour if the
 * viewport is too small to display the whole panel.
 *
 * Full documentation at http://simianstudios.com/portamento
 *
 * ----
 *
 * Uses the viewportOffset plugin by Ben Alman aka Cowboy:
 * http://benalman.com/projects/jquery-misc-plugins/#viewportoffset
 *
 * Uses a portion of CFT by Juriy Zaytsev aka Kangax:
 * http://kangax.github.com/cft/#IS_POSITION_FIXED_SUPPORTED
 *
 * Uses code by Matthew Eernisse:
 * http://www.fleegix.org/articles/2006-05-30-getting-the-scrollbar-width-in-pixels
 *
 * Builds on work by Remy Sharp:
 * http://jqueryfordesigners.com/fixed-floating-elements/
 *
 */

(function($) {

    $.fn.portamento = function(options) {

        // we'll use the window and document objects a lot, so
        // saving them as variables now saves a lot of function calls
        var thisWindow = $(window);
        var thisDocument = $(document);

        /**
         * NOTE by Kris - included here so as to avoid namespace clashes.
         *
         * jQuery viewportOffset - v0.3 - 2/3/2010
         * http://benalman.com/projects/jquery-misc-plugins/
         *
         * Copyright (c) 2010 "Cowboy" Ben Alman
         * Dual licensed under the MIT and GPL licenses.
         * http://benalman.com/about/license/
         */
        $.fn.viewportOffset = function() {
            var win = $(window);
            var offset = $(this).offset();

            return {
                left: offset.left - win.scrollLeft(),
                top: offset.top - win.scrollTop()
            };
        };

        /**
         *
         * A test to see if position:fixed is supported.
         * Taken from CFT by Kangax - http://kangax.github.com/cft/#IS_POSITION_FIXED_SUPPORTED
         * Included here so as to avoid namespace clashes.
         *
         */

        function positionFixedSupported() {
            var container = document.body;
            if (document.createElement && container && container.appendChild && container.removeChild) {
                var el = document.createElement("div");
                if (!el.getBoundingClientRect) {
                    return null;
                }
                el.innerHTML = "x";
                el.style.cssText = "position:fixed;top:100px;";
                container.appendChild(el);
                var originalHeight = container.style.height,
                    originalScrollTop = container.scrollTop;
                container.style.height = "3000px";
                container.scrollTop = 500;
                var elementTop = el.getBoundingClientRect().top;
                container.style.height = originalHeight;
                var isSupported = elementTop === 100;
                container.removeChild(el);
                container.scrollTop = originalScrollTop;
                return isSupported;
            }
            return null;
        }

        /**
         *
         * Get the scrollbar width by Matthew Eernisse.
         * http://www.fleegix.org/articles/2006-05-30-getting-the-scrollbar-width-in-pixels
         * Included here so as to avoid namespace clashes.
         *
         */

        function getScrollerWidth() {
            var scr = null;
            var inn = null;
            var wNoScroll = 0;
            var wScroll = 0;

            // Outer scrolling div
            scr = document.createElement('div');
            scr.style.position = 'absolute';
            scr.style.top = '-1000px';
            scr.style.left = '-1000px';
            scr.style.width = '100px';
            scr.style.height = '50px';
            // Start with no scrollbar
            scr.style.overflow = 'hidden';

            // Inner content div
            inn = document.createElement('div');
            inn.style.width = '100%';
            inn.style.height = '200px';

            // Put the inner div in the scrolling div
            scr.appendChild(inn);
            // Append the scrolling div to the doc
            document.body.appendChild(scr);

            // Width of the inner div sans scrollbar
            wNoScroll = inn.offsetWidth;
            // Add the scrollbar
            scr.style.overflow = 'auto';
            // Width of the inner div width scrollbar
            wScroll = inn.offsetWidth;

            // Remove the scrolling div from the doc
            document.body.removeChild(document.body.lastChild);

            // Pixel width of the scroller
            return (wNoScroll - wScroll);
        }

        // ---------------------------------------------------------------------------------------------------
        // get the definitive options
        var opts = $.extend({}, $.fn.portamento.defaults, options);

        // setup the vars accordingly
        var panel = this;
        var wrapper = opts.wrapper;
        var gap = opts.gap;
        var disableWorkaround = opts.disableWorkaround;
        var fullyCapableBrowser = positionFixedSupported();

        if (panel.length != 1) {
            // die gracefully if the user has tried to pass multiple elements
            // (multiple element support is on the TODO list!) or no elements...
            return this;
        }

        if (!fullyCapableBrowser && disableWorkaround) {
            // just stop here, as the dev doesn't want to use the workaround
            return this;
        }

        // wrap the floating panel in a div, then set a sensible min-height and width
        panel.wrap('<div id="portamento_container" />');
        var float_container = $('#portamento_container');
        float_container.css({
            'min-height': panel.outerHeight(),
            'width': panel.outerWidth()
        });

        // calculate the upper scrolling boundary
        var panelOffset = panel.offset().top;
        var panelMargin = parseFloat(panel.css('marginTop').replace(/auto/, 0));
        var realPanelOffset = panelOffset - panelMargin;
        var topScrollBoundary = realPanelOffset - gap;

        // a couple of numbers to account for margins and padding on the relevant elements
        var wrapperPaddingFix = parseFloat(wrapper.css('paddingTop').replace(/auto/, 0));
        var containerMarginFix = parseFloat(float_container.css('marginTop').replace(/auto/, 0));

        // do some work to fix IE misreporting the document width
        var ieFix = 0;

        var isMSIE = /*@cc_on!@*/
        0;

        if (isMSIE) {
            ieFix = getScrollerWidth() + 4;
        }

        // ---------------------------------------------------------------------------------------------------
        thisWindow.bind("scroll.portamento", function() {

            if (thisWindow.height() > panel.outerHeight() && thisWindow.width() >= (thisDocument.width() - ieFix)) { // don't scroll if the window isn't big enough
                var y = thisDocument.scrollTop(); // current scroll position of the document
                if (y >= (topScrollBoundary)) { // if we're at or past the upper scrolling boundary
                    if ((panel.innerHeight() - wrapper.viewportOffset().top) - wrapperPaddingFix + gap >= wrapper.height()) { // if we're at or past the bottom scrolling boundary
                        if (panel.hasClass('fixed') || thisWindow.height() >= panel.outerHeight()) { // check that there's work to do
                            panel.removeClass('fixed');
                            panel.css('top', (wrapper.height() - panel.innerHeight()) + 'px');
                        }
                    } else { // if we're somewhere in the middle
                        panel.addClass('fixed');

                        if (fullyCapableBrowser) { // supports position:fixed
                            panel.css('top', gap + 'px'); // to keep the gap
                        } else {
                            panel.clearQueue();
                            panel.css('position', 'absolute').animate({
                                top: (0 - float_container.viewportOffset().top + gap)
                            });
                        }
                    }
                } else {
                    // if we're above the top scroll boundary
                    panel.removeClass('fixed');
                    panel.css('top', '0'); // remove any added gap
                }
            } else {
                panel.removeClass('fixed');
            }
        });

        // ---------------------------------------------------------------------------------------------------
        thisWindow.bind("resize.portamento", function() {
            // stop users getting undesirable behaviour if they resize the window too small
            if (thisWindow.height() <= panel.outerHeight() || thisWindow.width() < thisDocument.width()) {
                if (panel.hasClass('fixed')) {
                    panel.removeClass('fixed');
                    panel.css('top', '0');
                }
            } else {
                thisWindow.trigger('scroll.portamento'); // trigger the scroll event to place the panel correctly
            }
        });

        // ---------------------------------------------------------------------------------------------------
        thisWindow.bind("orientationchange.portamento", function() {
            // if device orientation changes, trigger the resize event
            thisWindow.trigger('resize.portamento');
        });

        // ---------------------------------------------------------------------------------------------------
        // trigger the scroll event immediately so that the panel is positioned correctly if the page loads anywhere other than the top.
        thisWindow.trigger('scroll.portamento');

        // return this to maintain chainability
        return this;
    };

    // set some sensible defaults
    $.fn.portamento.defaults = {
        'wrapper': $('body'),
        // the element that will act as the sliding panel's boundaries
        'gap': 10,
        // the gap (in pixels) left between the top of the viewport and the top of the panel
        'disableWorkaround': false // option to disable the workaround for not-quite capable browsers
    };

})(jQuery);

(function() {

  $(function() {
    window.disqus_shortname = 'stefanoverna';
    window.__spr_config = {
      pid: "4f070745396cef0b3f0001fb",
      slide_logo: false,
      loc: '.article-content',
      header: 'FORSE POTREBBE INTERESSARTI',
      no_slide: !$("section.page-content").is(".page-single-article")
    };
    window._gaq = [['_setAccount', 'UA-5105763-1'], ['_trackPageview']];
    return $.each(['http://platform.twitter.com/widgets.js', 'http://stefanoverna.disqus.com/embed.js', 'http://stefanoverna.disqus.com/count.js', 'http://www.google-analytics.com/ga.js'], function(i, script) {
      return $.ajax({
        url: script,
        dataType: 'script',
        cache: true
      });
    });
  });

}).call(this);
(function() {

  $(function() {
    Hyphenator.config({
      displaytogglebox: false,
      minwordlength: 4
    });
    return Hyphenator.run();
  });

}).call(this);
(function() {

  $(function() {
    return $("section.page-content.index").each(function() {
      $(window).infinitescroll({
        url: "/blog/page-{{page}}.html",
        triggerAt: 150,
        appendTo: this
      });
      return $(this).bind("infinitescroll.finish", function() {
        $.ajax({
          url: 'http://platform.twitter.com/widgets.js',
          dataType: 'script',
          cache: true
        });
        return $.ajax({
          url: 'http://stefanoverna.disqus.com/count.js',
          dataType: 'script',
          cache: true
        });
      });
    });
  });

}).call(this);
(function() {

  $(function() {
    return $("section.page-content.archivio").each(function() {
      var $container, $original_content, results_template;
      $container = $(this);
      results_template = Hogan.compile('\
      <div class="right">\
        <h1>Risultati per "{{query}}"</h1>\
        <ul>\
          {{#results}}\
            <li>\
              <a href="{{link}}">{{title}}</a>\
            </li>\
          {{/results}}\
        </ul>\
      </div>\
    ');
      $original_content = $container.find(".right");
      return $("#query").change(function() {
        var query;
        query = $(this).val();
        if (query === "") {
          $container.find(".right").remove();
          return $container.append($original_content);
        } else {
          return $.getJSON("http://www.tapirgo.com/api/1/search.json?callback=?", {
            token: '4dc912b43f61b01c710001c1',
            query: query
          }).success(function(data) {
            var html;
            html = results_template.render({
              query: query,
              results: data
            });
            $container.find(".right").remove();
            return $container.append(html);
          });
        }
      });
    });
  });

}).call(this);
(function() {

  $(function() {
    return $('.page-single-article article').each(function() {
      var $article;
      $article = $(this);
      return $article.find("sidebar").portamento({
        wrapper: $article,
        gap: 24
      });
    });
  });

}).call(this);
(function() {



}).call(this);

